1 环境及背景

SpringBoot项目,使用poi、itextpdf将excel、ppt、word文件转为pdf,并对pdf文件进行签章;
对Excel文件加图片水印,并加密设置为只读。
下面的方法都是返回的byte数组,可视具体情况直接将文件输出。
office文件分为2003跟2007版本,所以处理的方法回有所区别。
还有很多其他方式可以实现转pdf,例如可以用第三方插件aspose处理office文件,但是需要收费;
可以使用spire,也是收费的;
还可以使用OpenOffice办公组件,加org.jodconverter,组件是适用于Linux,windows的。

2 pdf文件加盖签章

数字证书生成:

keytool -storepass 123456 -genkeypair -keyalg RSA -keysize 1024 -sigalg SHA1withRSA -validity 3650 -alias mycert -keystore my.keystore -dname "CN=www.sample.com, OU=sample, O=sample, L=BJ, ST=BJ, C=CN"

打开cmd窗口直接执行上面命令即可,会生成一个 my.keystore的文件。

/**
     * 对pdf进行签章
     * @param src pdf文件输入流
     * @param imgPath 签章图片路径
     * @param reason 签章原因
     * @param location 签章地点
     * @return
     * @throws GeneralSecurityException
     * @throws IOException
     * @throws DocumentException
     */
    public static byte[] sign(InputStream src,String imgPath,String reason,String location) throws GeneralSecurityException, IOException, DocumentException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] resBytes = null;
        try{
            //读取keystore ,获得私钥和证书链
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(ConvertImgToBase64Util.loadImgResource("templates/keystore/keystore.p12"),"123456".toCharArray());
            String alias = (String)keyStore.aliases().nextElement();
            PrivateKey PrivateKey = (PrivateKey) keyStore.getKey(alias, "123456".toCharArray());
            Certificate[] chain = keyStore.getCertificateChain(alias);

            PdfReader pdfReader = new PdfReader(src);
            Rectangle  rectangle= pdfReader.getPageSize(1);
            float urx =  rectangle.getRight()-100;
            float ury = rectangle.getTop()-100;
            float llx = urx-200;
            float lly = ury-200;
            PdfStamper stamper = PdfStamper.createSignature(pdfReader, baos, '', null, false);
            // 获取数字签章属性对象,设定数字签章的属性
            PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
            appearance.setReason(reason);
            appearance.setLocation(location);
            appearance.setVisibleSignature(new Rectangle(llx,lly,urx,ury), 1, "sign");
            //获取盖章图片
            byte[] imgBytes = ConvertImgToBase64Util.image2Bytes(imgPath);
            Image image = Image.getInstance(imgBytes);
            appearance.setSignatureGraphic(image);
            //设置认证等级
            appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
            //印章的渲染方式,这里选择只显示印章
            appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
            ExternalDigest digest = new BouncyCastleDigest();
            //签名算法,参数依次为:证书秘钥、摘要算法名称,例如MD5 | SHA-1 | SHA-2.... 以及 提供者
            ExternalSignature signature = new PrivateKeySignature(PrivateKey, DigestAlgorithms.SHA1, null);
            //调用itext签名方法完成pdf签章
            MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0,MakeSignature.CryptoStandard.CMS);
            resBytes = baos.toByteArray();
        }catch (Exception e){
            log.error("pdf文件签章异常:{}",e);
        }finally {
            try{
                if(baos != null){
                    baos.close();
                }
            }catch (IOException e){
                log.error("关闭io流异常:{}",e);
            }
        }
        return resBytes;
    }

上面代码出现的keystore.p12就是my.keystore

3 word转pdf


3.1 docx转pdf

public static byte[] docxToPdf(InputStream src) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] resBytes = null;
        String result;
        try {
            // pdf文件的尺寸
            Document pdfDocument = new Document(PageSize.A3, 72, 72, 72, 72);
            PdfWriter pdfWriter = PdfWriter.getInstance(pdfDocument, baos);
            XWPFDocument doc = new XWPFDocument(src);
            pdfWriter.setInitialLeading(20);
            java.util.List<XWPFParagraph> plist = doc.getParagraphs();
            pdfWriter.open();
            pdfDocument.open();
            for (int i = 0; i < plist.size(); i++) {
                XWPFParagraph pa = plist.get(i);
                java.util.List<XWPFRun> runs = pa.getRuns();
                for (int j = 0; j < runs.size(); j++) {
                    XWPFRun run = runs.get(j);
                    java.util.List<XWPFPicture> piclist = run.getEmbeddedPictures();
                    Iterator<XWPFPicture> iterator = piclist.iterator();
                    while (iterator.hasNext()) {
                        XWPFPicture pic = iterator.next();
                        XWPFPictureData picdata = pic.getPictureData();
                        byte[] bytepic = picdata.getData();
                        Image imag = Image.getInstance(bytepic);
                        pdfDocument.add(imag);
                    }
                    // 中文字体的解决
                    BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
                    Font font = new Font(bf, 11.0f, Font.NORMAL, BaseColor.BLACK);
                    String text = run.getText(-1);
                    byte[] bs;
                    if (text != null) {
                        bs = text.getBytes();
                        String str = new String(bs);
                        Chunk chObj1 = new Chunk(str, font);
                        pdfDocument.add(chObj1);
                    }
                }
                pdfDocument.add(new Chunk(Chunk.NEWLINE));
            }
            //需要关闭,不然无法获取到输出流
            pdfDocument.close();
            pdfWriter.close();
            resBytes = baos.toByteArray();
        } catch (Exception e) {
            log.error("docx转pdf文件异常:{}",e);
        }finally {
            try{
                if(baos != null){
                    baos.close();
                }
            }catch (IOException e){
                log.error("docx转pdf关闭io流异常:{}",e);
            }
        }
        return resBytes;
    }



3.2 doc转pdf

/**
     * doc转为pdf,先将doc转为html,再将html转为pdf,因为使用poi无法直接将doc转为pdf
     * @param src
     * @return
     */
    public static  byte[]  doc2pdf(InputStream src){
        byte[]  res = null;
        try{
            String html = OfficeToPdfUtil.doc2Html(src);
            html = OfficeToPdfUtil.formatHtml(html);
            res = OfficeToPdfUtil.htmlToPdf(html);
        }catch (Exception e){
            log.error("doc转pdf异常:{}",e);
        }
        return res;
    }


public class OfficeToPdfUtil {
    /**
     * html转为pdf
     * @param html
     * @return
     */
    public static byte[] htmlToPdf(String html) {
        com.itextpdf.text.Document document = null;
        ByteArrayInputStream bais = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] resBytes = null;
        try {
            document = new com.itextpdf.text.Document(PageSize.A4);
            PdfWriter writer = PdfWriter.getInstance(document, baos);
            document.open();
            bais = new ByteArrayInputStream(html.getBytes());
            XMLWorkerHelper.getInstance().parseXHtml(writer, document, bais,
                    Charset.forName("UTF-8"), new FontProvider() {
                        @Override
                        public boolean isRegistered(String s) {
                            return false;
                        }

                        @Override
                        public Font getFont(String s, String s1, boolean embedded, float size, int style, BaseColor baseColor) {
                            // 配置字体
                            Font font = null;
                            try {
                                BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
                                font = new Font(bf, size, style, baseColor);
                                font.setColor(baseColor);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            return font;
                        }
                    });
            document.close();
            writer.close();
            resBytes = baos.toByteArray();
        } catch (Exception e) {
            log.error("html转pdf异常:{}",e);
        } finally {
            if (document != null) {
                document.close();
            }
            if (bais != null) {
                try {
                    bais.close();
                } catch (IOException e) {
                    log.error("html转pdf关闭io流异常:{}",e);
                }
            }
        }
        return resBytes;
    }

    /**
     * doc文件转为html
     * @param inputStream
     * @return
     */
    public static String doc2Html(InputStream inputStream) {
        String content = null;
        ByteArrayOutputStream baos = null;
        try {
            HWPFDocument wordDocument = new HWPFDocument(inputStream);
            WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());
            wordToHtmlConverter.setPicturesManager(new PicturesManager() {
                @Override
                public String savePicture(byte[] content, PictureType pictureType, String suggestedName, float widthInches, float heightInches) {
                    return null;
                }
            });
            wordToHtmlConverter.processDocument(wordDocument);
            org.w3c.dom.Document htmlDocument = wordToHtmlConverter.getDocument();
            DOMSource domSource = new DOMSource(htmlDocument);
            baos = new ByteArrayOutputStream();
            StreamResult streamResult = new StreamResult(baos);

            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer serializer = tf.newTransformer();
            serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            serializer.setOutputProperty(OutputKeys.METHOD, "html");
            serializer.transform(domSource, streamResult);
        } catch (Exception e) {
            log.error("doc转html异常:{}",e);
        } finally {
            try {
                if (baos != null) {
                    content = new String(baos.toByteArray(), "utf-8");
                    baos.close();
                }
            } catch (Exception e) {
                log.error("doc转html关闭io流异常:{}",e);
            }
        }
        return content;
    }

    /**
     * 使用jsoup规范化html
     * @param html html内容
     * @return 规范化后的html
     */
    public static String formatHtml(String html) {
        org.jsoup.nodes.Document doc = Jsoup.parse(html);
        // 去除过大的宽度
        String style = doc.attr("style");
        if (StringUtils.isNotEmpty(style) && style.contains("width")) {
            doc.attr("style", "");
        }
        Elements divs = doc.select("div");
        for (org.jsoup.nodes.Element div : divs) {
            String divStyle = div.attr("style");
            if (StringUtils.isNotEmpty(divStyle) && divStyle.contains("width")) {
                div.attr("style", "");
            }
        }
        // jsoup生成闭合标签
        doc.outputSettings().syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml);
        doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml);
        return doc.html();
    }


4 ppt转pdf


4.1 pptx转pdf

public static byte[] pptxToPdf(InputStream src) {
        Document document = null;
        XMLSlideShow slideShow = null;
        FileOutputStream fileOutputStream = null;
        PdfWriter pdfWriter = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] resBytes = null;
        try {
            //使用输入流pptx文件
            slideShow = new XMLSlideShow(src);
            //获取幻灯片的尺寸
            Dimension dimension = slideShow.getPageSize();
            //创建一个写内容的容器
            document = new Document(PageSize.A3, 72, 72, 72, 72);
            //使用输出流写入
            pdfWriter = PdfWriter.getInstance(document, baos);
            //使用之前必须打开
            document.open();
            pdfWriter.open();
            PdfPTable pdfPTable = new PdfPTable(1);
            //获取幻灯片
            java.util.List<XSLFSlide> slideList = slideShow.getSlides();
            for (int i = 0, row = slideList.size(); i < row; i++) {
                //获取每一页幻灯片
                XSLFSlide slide = slideList.get(i);
                for (XSLFShape shape : slide.getShapes()) {
                    //判断是否是文本
                    if(shape instanceof XSLFTextShape){
                        // 设置字体, 解决中文乱码
                        XSLFTextShape textShape = (XSLFTextShape) shape;
                        for (XSLFTextParagraph textParagraph : textShape.getTextParagraphs()) {
                            for (XSLFTextRun textRun : textParagraph.getTextRuns()) {
                                textRun.setFontFamily("宋体");
                            }
                        }
                    }
                }
                //根据幻灯片尺寸创建图形对象
                BufferedImage bufferedImage = new BufferedImage((int)dimension.getWidth(), (int)dimension.getHeight(), BufferedImage.TYPE_INT_RGB);
                Graphics2D graphics2d = bufferedImage.createGraphics();
                graphics2d.setPaint(Color.white);
                graphics2d.setFont(new java.awt.Font("宋体", java.awt.Font.PLAIN, 12));
                //把内容写入图形对象
                slide.draw(graphics2d);
                graphics2d.dispose();
                //封装到Image对象中
                Image image = Image.getInstance(bufferedImage, null);
                image.scalePercent(50f);
                // 写入单元格
                pdfPTable.addCell(new PdfPCell(image, true));
                document.add(image);
            }
            document.close();
            pdfWriter.close();
            resBytes = baos.toByteArray();
        } catch (Exception e) {
            log.error("pptx转pdf异常:{}",e);
        } finally {
            try {
                if (baos != null) {
                     baos.close();
                }
            } catch (IOException e) {
                log.error("pptx转pdf关闭io流异常:{}",e);
            }
        }
        return resBytes;
    }



4.2 ppt转pdf

/**
     * 将ppt转为pdf,兼容ppt和pptx
     * @param is
     * @return
     */
    public static byte[] ppt2pdf(InputStream is) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] resBytes = null;
        try {
            Document pdfDocument = new Document();
            PdfWriter pdfWriter = PdfWriter.getInstance(pdfDocument, baos);
            HSLFSlideShow hslfSlideShow = new HSLFSlideShow(is);
            double zoom = 2;
            if (hslfSlideShow == null) {
                XMLSlideShow ppt = new XMLSlideShow(is);
                if (ppt == null) {
                    throw new NullPointerException("获取ppt文件数据失败");
                }
                Dimension pgsize = ppt.getPageSize();
                List<XSLFSlide> slide = ppt.getSlides();
                AffineTransform at = new AffineTransform();
                at.setToScale(zoom, zoom);
                pdfDocument.setPageSize(new Rectangle((float) pgsize.getWidth(), (float) pgsize.getHeight()));
                pdfWriter.open();
                pdfDocument.open();
                PdfPTable table = new PdfPTable(1);
                for (XSLFSlide xslfSlide : slide) {
                    BufferedImage img = new BufferedImage((int) Math.ceil(pgsize.width * zoom), (int) Math.ceil(pgsize.height * zoom), BufferedImage.TYPE_INT_RGB);
                    Graphics2D graphics = img.createGraphics();
                    graphics.setTransform(at);

                    graphics.setPaint(Color.white);
                    graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
                    xslfSlide.draw(graphics);
                    graphics.getPaint();
                    Image slideImage = Image.getInstance(img, null);
                    table.addCell(new PdfPCell(slideImage, true));
                }
                ppt.close();
                pdfDocument.add(table);
                pdfDocument.close();
                pdfWriter.close();
                resBytes = baos.toByteArray();
                return resBytes;
            }
            Dimension pgsize = hslfSlideShow.getPageSize();
            List<HSLFSlide> slides = hslfSlideShow.getSlides();
            pdfDocument.setPageSize(new Rectangle((float) pgsize.getWidth(), (float) pgsize.getHeight()));
            pdfWriter.open();
            pdfDocument.open();
            AffineTransform at = new AffineTransform();
            PdfPTable table = new PdfPTable(1);
            for (HSLFSlide hslfSlide : slides) {
                BufferedImage img = new BufferedImage((int) Math.ceil(pgsize.width * zoom), (int) Math.ceil(pgsize.height * zoom), BufferedImage.TYPE_INT_RGB);
                Graphics2D graphics = img.createGraphics();
                graphics.setTransform(at);

                graphics.setPaint(Color.white);
                graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
                hslfSlide.draw(graphics);
                graphics.getPaint();
                Image slideImage = Image.getInstance(img, null);
                table.addCell(new PdfPCell(slideImage, true));
            }
            hslfSlideShow.close();
            pdfDocument.add(table);
            pdfDocument.close();
            pdfWriter.close();
            resBytes = baos.toByteArray();
            return resBytes;
        } catch (Exception e) {
            log.error("ppt转为pdf异常:{}",e);
        }
        return resBytes;
    }


这里兼容有点问题,当HSLFSlideShow hslfSlideShow = new HSLFSlideShow(is)无法创建对象时,io流无法再被使用了,则无法再去创建XMLSlideShow对象。但是可以使用此方法转ppt后缀的文件。

5 excel文件加图片水印,并加密


@Slf4j
public class ExcelWaterMakerUtils {

    /**
     * 给Excel添加图片水印,并加密,返回新文件的byte数组
     * @param imgPath
     * @param fileName
     * @param fis
     * @return
     */
    public static byte[] addWaterMaker(String imgPath,String fileName, InputStream fis){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        BufferedImage bufferImg = null;
        byte[] resBytes = null;
        try {
            Workbook workbook;
            if (fileName.endsWith(".xlsx")){
                workbook = new XSSFWorkbook(fis);
            } else {
                workbook = new HSSFWorkbook(fis);
            }
            //设置Excel为只读
            String password = RandomStringUtils.random(6,false,true);
            int sheetNumbers = workbook.getNumberOfSheets();
            short rightNum = 3;
            short leftNum = 2;
            for (int i = 0; i < sheetNumbers; i++) {
                Sheet sheet =  workbook.getSheetAt(i);
                sheet.protectSheet(password);
                Row row = sheet.getRow(1);
                if(row == null){
                    break;
                }
                short cellLastNum = row.getLastCellNum();
                rightNum = (short) (cellLastNum-1);
                leftNum = (short) (cellLastNum - 2);
                ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
                InputStream img = ConvertImgToBase64Util.loadImgResource(imgPath);
                bufferImg = ImageIO.read(img);
                //第二个参数将会决定插入图片形式,如果是一个png的图片,背景透明,但是此处设置为jpg格式将会自动添加黑色背景
                ImageIO.write(bufferImg, "png", byteArrayOut);
                //画图的顶级管理器,一个sheet只能获取一个
                Drawing drawing = sheet.createDrawingPatriarch();
                //anchor主要用于设置图片的属性
                ClientAnchor anchor = drawing.createAnchor(0, 0, 255, 255,leftNum, 1, rightNum, 1);
//            anchor.setAnchorType(2);
                //插入图片
                drawing.createPicture(anchor, workbook.addPicture(byteArrayOut.toByteArray(), HSSFWorkbook.PICTURE_TYPE_PNG));
                Picture pic = drawing.createPicture(anchor,
                        workbook.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_PNG));
                pic.resize();
            }
            workbook.write(baos);
            resBytes = baos.toByteArray();
            fis.close();
            baos.close();
        } catch (Exception e) {
            log.error("Excel盖章异常:{}",e);
        }finally{
            try{
                if(fis != null){
                    fis.close();
                }
                if(baos != null){
                    baos.close();
                }
            }catch (IOException ioe){
                log.error("Excel盖章关闭io流异常:{}",ioe);
            }
        }
        return resBytes;
    }
}


图片的位置是根据excel表的行列来定位的,所以不同的excel文件图片的大小可能会有变化,需要优化下,我优化了很久没能实现,将就用着吧。。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐