【Java + itextpdf】实现Java生成pdf文件(以ByteArrayOutputStream形式传输)

前段时间猫哥实现了用Java生成Excel文件,并以ByteArrayOutputStream的形式传输至JavaMail附件中。在这里再整理一下生成pdf文件并同样以ByteArrayOutputStream的形式传输至JavaMail附件中的方法。

其中,最主要的重点就是:

  1. pdf中的字体格式(由于itext对中文支持较差,而若直接采用itext自带的字体会导致最终打印内容为空白,因此需要自己下载中文字体添加到项目resources中并引用,项目中猫哥用了自己下的simhei.ttf(黑体))
  2. pdf页码的实现(最终的实现格式:第x页/共x页,关键是要在document关闭后重新读取计算总页数)

直接上代码

package com.catchgo.devicejob.service.impl;

import com.catchgo.devicejob.core.model.bean.print.CloudPrintBean;
import com.catchgo.devicejob.core.model.bean.print.PrintBean;
import com.catchgo.devicejob.core.model.bean.print.ReplenishmentBean;
import com.catchgo.devicejob.core.model.constant.PrintStatus;
import com.catchgo.devicejob.core.model.constant.PrintTaskStatus;
import com.catchgo.devicejob.dao.CloudPrintMapper;
import com.catchgo.devicejob.dao.CloudPrintTaskMapper;
import com.catchgo.devicejob.report.dao.ReportCloudPrintMapper;
import com.catchgo.devicejob.service.SendMailService;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.xxl.job.core.log.XxlJobLogger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.Email;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;

/**
 * @Description: 商品上次补货后的销售单
 * @Author: Li Yacheng
 * @Date: 2020/6/23 16:28
 */
@Service("PrintReplenishmentListPDFService")
@ConfigurationProperties(prefix = "email")
@Validated
@Async("taskExecutor")
@Component
public class PrintReplenishmentListPDFService {
    @Autowired
    private CloudPrintMapper cloudPrintMapper;

    @Autowired
    private CloudPrintTaskMapper cloudPrintTaskMapper;
    @Autowired
    private ReportCloudPrintMapper reportCloudPrintMapper;

    @Email
    @Value("${email.address}")
    private String email;

    @Value("${email.password}")
    private String password;

    private final static String type = "cloud_print_device_1";

    private final static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public PdfTemplate tpl; //模板用来固定显示数据
    public PdfTemplate tpl2; //模板用来固定显示数据
    private static Font CFont; //中文字体
    private static Font EFont; //英文字体
    private static Font KFont; //楷体
    private static Font TittleFont; //中文字体
    private static BaseFont bfChinese;
    static{
        try {
            bfChinese = BaseFont.createFont("/fonts/simhei.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            TittleFont = new Font(bfChinese,18,Font.UNDEFINED);
            CFont = new Font(bfChinese,10,Font.UNDEFINED);
            EFont = new Font(Font.FontFamily.TIMES_ROMAN,12,Font.UNDEFINED);
            KFont = new Font(bfChinese,11,Font.BOLD);
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void print(CloudPrintBean cloudPrintBean) throws DocumentException {
        int taskStatus = 0;
        Long taskId = cloudPrintBean.getTaskId();
        String addressFrom = cloudPrintBean.getAddressFrom();
        String addressTo = cloudPrintBean.getAddressTo();
        List<PrintBean> printBeanList = cloudPrintBean.getContentList();
        List<Long> deviceId = new ArrayList<>();
        List<String> content = new ArrayList<>();
        for (PrintBean printBean : printBeanList) {
            deviceId.add(printBean.getId());
            content.add(printBean.getParamStr());
        }

        //content去重
        List<String> listNew = new ArrayList<String>(new TreeSet<String>(content));
        List<ReplenishmentBean> replenishmentBeanList = new ArrayList<>();
        List<Long> emptyId = new ArrayList<>();
        BigDecimal zero= new BigDecimal("0");
        for (PrintBean printBean : printBeanList) {
            List<ReplenishmentBean> replenishmentBean = reportCloudPrintMapper.selectReplenishmentList(printBean.getParamStr());
            if(replenishmentBean.size()>0){
                for(ReplenishmentBean replenishmentBean1 : replenishmentBean){
                    if (replenishmentBean1 != null && replenishmentBean1.getPrice().compareTo(zero)==1) {
                        replenishmentBean1.setId(printBean.getId());
                        //查询商品最近一次的购买时间
                        String deviceName = replenishmentBean1.getDeviceName();
                        Long goodsId = replenishmentBean1.getGoodsId();
//                        SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
                        String dtBeg = formatter.format(new Date());
                        if(!StringUtils.isEmpty(deviceName)&&goodsId!=null){
                            Date latestBuyTime = cloudPrintMapper.selectLastestBuyTimeBuyGoodsIdAndDeviceName(goodsId,deviceName);
                            if(latestBuyTime!=null){
                                dtBeg = formatter.format(latestBuyTime);;
                            }
                        }
                        replenishmentBean1.setLatestSaleTime(dtBeg);
                        replenishmentBeanList.add(replenishmentBean1);
                    }
                }
            }else {
                emptyId.add(printBean.getId());
            }
        }
        if(emptyId.size()>0){
            //将对应的内容根据ID更新状态该为无打印内容
            cloudPrintMapper.updatePrintStatusById(emptyId, PrintStatus.NOTHING.getCode());
        }

        XxlJobLogger.log("开始创建打印商品上次补货后的销售单任务...");

        //分页
        int numStatic = 9999999;
        int pageNum = (int) Math.ceil(replenishmentBeanList.size() / (numStatic * 1.0));
        String num = "";
        for (int i = 0; i < pageNum; i++) {
            int status = 0;
            if (pageNum > 1) {
                num = String.valueOf(i + 1);
            }
            int startIndex = i * numStatic;
            int endIndex;
            if (i != pageNum - 1) {
                endIndex = startIndex + numStatic;
            } else {
                endIndex = replenishmentBeanList.size();
            }
            List<Long> id = new ArrayList<>();
            List<String> deviceName = new ArrayList<>();
            List<String> place = new ArrayList<>();
            List<String> goodsName = new ArrayList<>();
            List<String> goodsCount = new ArrayList<>();
            List<String> goodsPrice = new ArrayList<>();
            List<String> price = new ArrayList<>();
            List<String> replenishmentTime = new ArrayList<>();
            List<String> lastestSaleTime = new ArrayList<>();

            for (int j = startIndex; j < endIndex; j++) {
                String str = "-";
                if(!StringUtils.isEmpty(replenishmentBeanList.get(j).getId())){
                    id.add(replenishmentBeanList.get(j).getId());
                }else {
                    id.add(0L);
                }
                if(!StringUtils.isEmpty(replenishmentBeanList.get(j).getDeviceName())){
                    deviceName.add(replenishmentBeanList.get(j).getDeviceName());
                }else {
                    deviceName.add(str);
                }
                if(!StringUtils.isEmpty(replenishmentBeanList.get(j).getPlace())){
                    place.add(replenishmentBeanList.get(j).getPlace());
                }else {
                    place.add(str);
                }
                if(!StringUtils.isEmpty(replenishmentBeanList.get(j).getGoodsName())){
                    goodsName.add(replenishmentBeanList.get(j).getGoodsName());
                }else {
                    goodsName.add(str);
                }
                if(replenishmentBeanList.get(j).getGoodsCount()!=null){
                    goodsCount.add(String.valueOf(replenishmentBeanList.get(j).getGoodsCount()));
                }else {
                    goodsCount.add(str);
                }
                if(replenishmentBeanList.get(j).getGoodsPrice()!=null){
                    goodsPrice.add(String.valueOf(replenishmentBeanList.get(j).getGoodsPrice()));
                }else {
                    goodsPrice.add(str);
                }
                if(replenishmentBeanList.get(j).getPrice()!=null){
                    price.add(String.valueOf(replenishmentBeanList.get(j).getPrice()));
                }else {
                    price.add(str);
                }
                if(replenishmentBeanList.get(j).getReplenishmentTime()!=null){
                    String dateString = formatter.format(replenishmentBeanList.get(j).getReplenishmentTime());
                    replenishmentTime.add(dateString);
                }else {
                    replenishmentTime.add(str);
                }
                if(replenishmentBeanList.get(j).getLatestSaleTime()!=null){
                    String dateString = replenishmentBeanList.get(j).getLatestSaleTime();
                    lastestSaleTime.add(dateString);
                }else {
                    lastestSaleTime.add(str);
                }
            }

            List<String> deviceNameTemp = new ArrayList<String>(new TreeSet<String>(deviceName));
            Collections.reverse(deviceNameTemp);
            List<String> placeTemp = new ArrayList<>();
            List<String> replenishmentTimeTemp = new ArrayList<>();
            for(int k = 0;k<deviceNameTemp.size();k++){
                for (int j = startIndex; j < endIndex; j++) {
                    if(replenishmentBeanList.get(j).getDeviceName().equals(deviceNameTemp.get(k))){
                        String placeStr = "-";
                        if(replenishmentBeanList.get(j).getPlace()!=null){
                            placeStr=replenishmentBeanList.get(j).getPlace();
                        }
                        placeTemp.add(placeStr);
                        String dateStr = "=";
                        if(replenishmentBeanList.get(j).getReplenishmentTime()!=null){
                            dateStr = formatter.format(replenishmentBeanList.get(j).getReplenishmentTime());
                        }
                        replenishmentTimeTemp.add(dateStr);
                        break;
                    }
                }
            }

            //发送的邮件内容
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            Document document = new Document(PageSize.A4);// 可配其余4个参数,如(rectPageSize,60,60,60,60)页面边距
            //定义writer写入页码等并指明文件输出流输出到一个文件
            PdfWriter writer = PdfWriter.getInstance(document,os);//将PDF文档对象写入到流

            document.open();

            //创建模板存放页码内容以及模块大小
            tpl = writer.getDirectContent().createTemplate(100, 100);
            tpl2 = writer.getDirectContent().createTemplate(100, 100);

            PdfPTable table = new PdfPTable(1);
            table.setWidthPercentage(100); // 宽度100%填充
            table.setSpacingBefore(10f); // 前间距
            table.setSpacingAfter(10f); // 后间距

            // 设置列宽
            float[] columnWidths = { 1.5f };
            table.setWidths(columnWidths);
            table.getDefaultCell().setBorder(0); //表格边框

            PdfPCell pdfPCell;

            //显示固定页码位置
            getTotalNum(writer);

            //第一行(标题)
            pdfPCell = new PdfPCell();
            Font fontChinese = new Font(TittleFont);
            Paragraph faxparagrah = new Paragraph("每个商品上次补货后的销售单\n",fontChinese);
            pdfPCell.setPhrase(faxparagrah);
            table.addCell(pdfPCell).setBorder(0);

            pdfPCell = new PdfPCell();
            Font fontChineseContent = new Font(CFont);
            String printTime = formatter.format(new Date());
            Paragraph faxparagrahTime = new Paragraph("(单据生成时间:"+printTime+")\n",fontChineseContent);
            pdfPCell.setPhrase(faxparagrahTime);
            table.addCell(pdfPCell).setBorder(0);

            document.add(table);

            for(int j = 0;j<deviceNameTemp.size();j++){
                //设备地点展示表
                table = new PdfPTable(1);
                table.setWidthPercentage(100); // 宽度100%填充
                table.setSpacingBefore(10f); // 前间距
                table.setSpacingAfter(10f); // 后间距

                //设置列宽
                float[] columnWidth = { 1.5f };
                table.setWidths(columnWidth);
                table.getDefaultCell().setBorder(0); //表格边框

                //第一行(设备,地点)
                pdfPCell = new PdfPCell();
                Paragraph faxparagrah1 = new Paragraph();
                faxparagrah1.add(new Paragraph("设备:"+deviceNameTemp.get(j)+"\n",fontChineseContent));
                faxparagrah1.add(new Paragraph("地点:"+placeTemp.get(j)+"\n",fontChineseContent));
                pdfPCell.setPhrase(faxparagrah1);
                table.addCell(pdfPCell).setBorder(0);
                document.add(table);

                //内容表
                table = new PdfPTable(6);
                table.setWidthPercentage(100); // 宽度100%填充
                table.setSpacingBefore(10f); // 前间距
                table.setSpacingAfter(10f); // 后间距

                // 设置列宽
                float[] columnWidth2 = { 0.5f,0.25f,0.25f,0.25f,0.5f,0.5f };
                table.setWidths(columnWidth2);
                table.getDefaultCell().setBorder(0); //表格边框
                //第一行(表头)
                pdfPCell = createPdfPCell("商品名称",true,fontChineseContent);
                pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                table.addCell(pdfPCell);
                pdfPCell = createPdfPCell("销量",true,fontChineseContent);
                pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                table.addCell(pdfPCell);
                pdfPCell = createPdfPCell("单价",true,fontChineseContent);
                pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                table.addCell(pdfPCell);
                pdfPCell = createPdfPCell("销售额",true,fontChineseContent);
                pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                table.addCell(pdfPCell);
                pdfPCell = createPdfPCell("上次补货时间",true,fontChineseContent);
                pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                table.addCell(pdfPCell);
                pdfPCell = createPdfPCell("上次售出时间",true,fontChineseContent);
                pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                table.addCell(pdfPCell);

                for (int k = 0; k < deviceName.size(); k++) {
                    if(deviceName.get(k).equals(deviceNameTemp.get(j))){
                        //内容
                        pdfPCell = createPdfPCell(goodsName.get(k),true,fontChineseContent);
                        pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                        table.addCell(pdfPCell);
                        pdfPCell = createPdfPCell(goodsCount.get(k),true,fontChineseContent);
                        pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                        table.addCell(pdfPCell);
                        pdfPCell = createPdfPCell(goodsPrice.get(k),true,fontChineseContent);
                        pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                        table.addCell(pdfPCell);
                        pdfPCell = createPdfPCell(price.get(k),true,fontChineseContent);
                        pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                        table.addCell(pdfPCell);
                        pdfPCell = createPdfPCell(replenishmentTime.get(k),true,fontChineseContent);
                        pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                        table.addCell(pdfPCell);
                        pdfPCell = createPdfPCell(lastestSaleTime.get(k),true,fontChineseContent);
                        pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
                        table.addCell(pdfPCell);
                    }
                }

                document.add(table);
            }

            document.close();

            List<PdfReader> readers = new ArrayList<>();
            try {
                readers.add(new PdfReader(os.toByteArray()));

            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                os = finish(readers);
            } catch (Exception e) {
                e.printStackTrace();
            }

            //html 转 word
            String uuid = "1.pdf";

            ByteArrayInputStream a = new ByteArrayInputStream(os.toByteArray());

            //指明让那个smtp转发
            SendMailService themail = new SendMailService("smtp.163.com");

            //校验身份
            themail.setNeedAuth(true);

            //邮件标题
            if (!themail.setSubject("每个商品上次补货后的销售单")) {
                status = -1;
            }

            //将要发送的内容放入邮件体
            if (!themail.setBody("")) {
                status = -1;
            }

            //发送到哪里
            if (!themail.setTo(addressTo, email)) {
                status = -1;
            }

            //谁发送的
            if (!themail.setFrom(addressFrom)) {
                status = -1;
            }

            //添加附件
            if(!themail.addFileAffix2(a,uuid)){
                status = -1;
            }

            //将邮箱用户名和密码放入邮件体
            themail.setNamePass(email, password);

            //发送
            if (!themail.sendout()) {
                status = -1;
            }

            for (String device : deviceName) {
                for (String deviceSet : listNew) {
                    if (device.equals(deviceSet)) {
                        //log表记录
                        cloudPrintMapper.insertPrintLog(device, type);
                        listNew.remove(deviceSet);
                        break;
                    }
                }
            }

            if (status == -1) {
                //更新设备的打印状态为失败
                cloudPrintMapper.updatePrintStatusById(id, PrintStatus.ERROR.getCode());
            }else {
                taskStatus++;
                //更新设备的打印状态为已打印
                cloudPrintMapper.updatePrintStatusById(id, PrintStatus.PRINTED.getCode());
                if(pageNum == 1){
                    XxlJobLogger.log("商品上次补货后的销售单任务已成功发送至打印机!");
                }else {
                    int current = i+1;
                    XxlJobLogger.log("商品上次补货后的销售单任务已成功发送至打印机!(" + current+ "-" + pageNum + ")");
                }
            }
        }
        if(taskStatus==pageNum){
            //打印任务表直接更新为成功
            cloudPrintTaskMapper.updatePrintStatus(taskId, PrintTaskStatus.SUCCESS.getCode());
        }else if(taskStatus==0){
            //打印任务表直接更新为失败
            cloudPrintTaskMapper.updatePrintStatus(taskId, PrintTaskStatus.FAIL.getCode());
        }else {
            //打印任务表直接更新为部分成功
            cloudPrintTaskMapper.updatePrintStatus(taskId, PrintTaskStatus.PARTIAL_SUCCESS.getCode());
        }
    }


    protected ByteArrayOutputStream finish(List<PdfReader> readers) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Document doc = new Document(PageSize.A4);
        PdfCopy copy = new PdfCopy(doc, out);
        doc.open();

        int currentPage = 1, totalPage = readers.stream().mapToInt(PdfReader::getNumberOfPages).sum();
        for (PdfReader reader : readers) {
            PdfImportedPage page;
            PdfCopy.PageStamp stamp;
            int pages = reader.getNumberOfPages();
            for (int index = 1; index <= pages; index++) {
                doc.newPage();
                page = copy.getImportedPage(reader, index);
                stamp = copy.createPageStamp(page);
                ColumnText.showTextAligned(stamp.getUnderContent(), Element.ALIGN_CENTER,
                        new Phrase(addFont8(String.format("第%d页/共%d页", currentPage++, totalPage))), 300f, 16f, 0f);
                stamp.alterContents();

                copy.addPage(page);
            }

            reader.close();
        }

        doc.close();
        copy.close();

        return out;
    }

    protected Paragraph addFont8(String content) {
        return addText(content, CFont);
    }

    private Paragraph addText(String content, Font font) {
        Paragraph paragraph = new Paragraph(content, font);
        paragraph.setAlignment(Element.ALIGN_LEFT);
        return paragraph;
    }

    private void getTotalNum(PdfWriter writer ) {
        PdfContentByte cb = writer.getDirectContent();
        cb.saveState();
        //创建以及固定显示总页数的位置
        cb.addTemplate(tpl2, 205, 720);//定位“y页” 在具体的页面调试时候需要更改这xy的坐标

        cb.stroke();
        cb.restoreState();
        cb.closePath();
    }

    private static PdfPCell createPdfPCell(String text ,Boolean border, Font font) {
        PdfPCell pdfPCell = new PdfPCell();
        pdfPCell.setPhrase(new Paragraph(text, font));

        return pdfPCell;
    }
}

最终效果图:

最后顺便记录一下iText输出中文的三种字体选择方式:

  1. 使用iTextAsian.jar中的字体(亲测会碰到预览文件有文字,打印却空白的情况)
    BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);
  2. 使用Windows系统字体(TrueType)(直接描述就是把字体下载到本地然后调用)
    BaseFont.createFont("C:/WINDOWS/Fonts/SIMYOU.TTF", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
  3. 使用资源字体(ClassPath)(直接描述就是把字体导入项目中然后调用)
    BaseFont.createFont("./SIMYOU.TTF", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值