Java 邮政EMS快递面打印实现 笔记

最近搞定了一个比较头疼的事情,邮政快递面单打印的需求。

需求:系统需要支持邮政下单、打印快递面单。

坑爹之处:邮政就是牛叉任性,其他家快递都可以提供面单查询接口,直接返回面单PDF数据,可以直接打印。邮政EMS就是丢给你一个面单规格说明书。你需要自己画出面单。

在网站查找了好久关于EMS面单实现的资料分享很少,只能死磕,最后还是实现了。这里把整个实现流程分享出来

邮政EMS面单打印实现步骤:

1、请求EMS下单接口获取面单号

2、Java Graphics2D 画出快递面单图片。(这一步利用 Graphics2D 绘制出来 )

3、将图片生成PDF文件。

4、页面获取PDF文件打印

详细的实现步骤以及

一、下单接口请求数据

根据邮政提供的相关接口文档组装数据,发送请求。

/**
 * 下单接口
 *
 * @param order
 * @return
 */
@Override
public boolean placeOrder(BatOrder order, HospitalInformation hospitalInformation) throws IOException, NoSuchAlgorithmException, DocumentException {

    String logistics_interface = "<OrderNormal>" +
            "<created_time>" + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss") + "</created_time>" +
            "<logistics_provider>B</logistics_provider>" +
            "<ecommerce_no>CICPSSYSTEM</ecommerce_no>" +
            "<ecommerce_user_id>2</ecommerce_user_id>" +
            "<sender_type>1</sender_type>" +
            "<inner_channel>0</inner_channel>" +
            "<logistics_order_no>" + order.getId().trim() + "</logistics_order_no>" +
            "<contents_attribute>3</contents_attribute>" +
            "<base_product_no>1</base_product_no>" +
            "<biz_product_no></biz_product_no>" +
            "<insurance_flag>1</insurance_flag>" +
            "<payment_mode>1</payment_mode>" +
            "<receipt_flag>1</receipt_flag>" +
            "<valuable_flag>0</valuable_flag>" +
            "<sender_safety_code>0</sender_safety_code>" +
            "<sender>" +
            "<name>" + hospitalInformation.getHospitalName() + "</name>" +
            "<post_code></post_code>" +
            "<phone>" + hospitalInformation.getPhoneNumber() + "</phone>" +
            "<mobile>" + hospitalInformation.getPhoneNumber() + "</mobile>" +
            "<prov>" + hospitalInformation.getProvince() + "</prov>" +
            "<city>" + hospitalInformation.getCity() + "</city>" +
            "<county>" + hospitalInformation.getCounty() + "</county>" +
            "<address>" + hospitalInformation.getArea() + "</address>" +
            "</sender>" +
            "<receiver>" +
            "<name>" + order.getAddressName() + "</name>" +
            "<post_code/>" +
            "<phone>" + order.getAddressPhone() + "</phone>" +
            "<mobile>" + order.getAddressPhone() + "</mobile>" +
            "<prov>" + order.getProvince() + "</prov>" +
            "<city>" + order.getCity() + "</city>" +
            "<county>" + order.getCounty() + "</county>" +
            "<address>" + order.getAddress() + "</address>" +
            "</receiver>" +
            "<cargos>" +
            "<Cargo>" +
            "<cargo_name>文件</cargo_name>" +
            "<cargo_category/><cargo_quantity/>" +
            "<cargo_value/><cargo_weight/>" +
            "</Cargo>" +
            "</cargos>" +
            "</OrderNormal>";
    //签名
    String data_digest = makeSignEMS(logistics_interface, hospitalInformation.getYunda_key1());

    HashMap<String, Object> paramMap = new HashMap<>();
    paramMap.put("msg_type", "邮政提供");
    paramMap.put("ecCompanyId", "邮政提供");
    paramMap.put("data_digest", data_digest);
    paramMap.put("logistics_interface", logistics_interface);
    String xml = HttpUtil.get(placeOrderUrl, paramMap);
    log.info("邮政下单接口返回值:{}", xml);
    
    //解析邮政返回的回执信息
    //返回值格式:
    //   <responses>
    //       <responseItems>
    //        <response>
    //            <success>true</success>
    //            <waybill_no>1200002779901</waybill_no>
    //            <routeCode>310-新A-沙区02-沙区揽投-*</routeCode>
    //            <packageCode></packageCode>
    //            <packageCodeName>310-新A</packageCodeName>
    //            <markDestinationCode>310-新A</markDestinationCode>
    //            <markDestinationName>310-新A</markDestinationName>
    //       </response>
    //    </responseItems>
    //  </responses>

    Map<String, String> data = XMLUtil.getDateFromEMSXML(xml);

    if (data.get("success").equals("true")) {
        //修改订单状态
        order.setTracking_number(data.get("waybill_no"));
        order.setUpdateTime(new Date());
        order.setFilePath(data.get("routeCode"));
        order.setSendDate(new Date());
        int ret = batOrderService.updateBatOrder(order);
        if (ret > 0) {
            createPDF(order,hospitalInformation);
            return true;
        } else {
            log.error("修改更新数据库状态失败");
            return false;
        }
    } else {
        log.error("邮政 下单取号接口失败");
        return false;
    }
}

/**
 * 邮政 生成签名的方法
 *
 * @param data
 * @param parentId
 * @return
 * @throws NoSuchAlgorithmException
 * @throws UnsupportedEncodingException
 */
public String makeSignEMS(String data, String parentId)
        throws NoSuchAlgorithmException, UnsupportedEncodingException {
    MessageDigest md = MessageDigest.getInstance("MD5");
    BASE64Encoder base64en = new BASE64Encoder();
    String Ret = base64en.encode(MessageDigest.getInstance("MD5").digest((data + parentId).getBytes("UTF-8")));
    return Ret;
}

解析接口返回数据方法

/**
 * 从XML字符串中获取数据
 * @param xml 例如: String xml2="<responses><responseItems><response><success>true</success><waybill_no>1200002779901</waybill_no><routeCode>310-新A-沙区02-沙区揽投-*</routeCode><packageCode></packageCode><packageCodeName>310-新A</packageCodeName><markDestinationCode>310-新A</markDestinationCode><markDestinationName>310-新A</markDestinationName></response></responseItems></responses>";
 * @return
 */
public static Map<String, String> getDateFromEMSXML(String xml) {
    Map<String, String> retMap = new HashMap<String,String>();
    Document document = null;
    try {
        //得到document
        document = DocumentHelper.parseText(xml);
        //得到第一个根节点
        Element root = document.getRootElement();
        retMap.put("success", root.element("responseItems").element("response").element("success").getText());
        retMap.put("waybill_no", root.element("responseItems").element("response").element("waybill_no").getText());
        retMap.put("routeCode", root.element("responseItems").element("response").element("routeCode").getText());
    } catch (Exception e) {
        log.error("从传输报文获取数据异常:" + e);
    }
    return retMap;
}

二、Java Graphics2D 画出快递面单图片

邮政EMS面单设计规范,本次实现的是热敏100* 150 (单位 mm)标准快递打印效果。

这里需要注意单位毫米、像素之间的转换。因为规范说明采用毫米单位,代码中使用了像素单位。这里踩坑了。

他们之间想要实现转换,首先固定分辨率。一般快递单打印的热敏打印机分辨率是203 ,这里我就是固定203分辨率。

邮政EMS面单设计规范(邮政会提供)

这一步利用 Java Graphics2D 绘制出来,比较返回,难度就是各种尺寸的把握。

package com.chunge.hospital.util;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Date;
import javax.imageio.ImageIO;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import com.chunge.hospital.domain.BatOrder;
import com.chunge.hospital.domain.HospitalInformation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import org.springframework.util.ResourceUtils;

/**
 * 生成电子面单图片工具
 *
 * @author chunge
 * @version 1.0
 * @time 2021/08/01
 */
@Slf4j
public class EMSGenerateUtil {

    //图片的宽度
    public static final int IMG_WIDTH = 799;

    //图片的宽度
    public static final int IMG_HEIGHT = 1199;

    //LOGO的宽度
    public static final int LOGO_WIDTH = 220;
    //LOGO高度
    public static final int LOGO_HEIGHT = 70;
    //Logo路径
    public static final String LOGO_PATH = "D:\\image\\ems_logo.png";

    public static BufferedImage image;

    public static void createImage(String fileLocation) {
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;
        try {
            fos = new FileOutputStream(fileLocation);
            bos = new BufferedOutputStream(fos);
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos);
            encoder.encode(image);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bos != null) bos.close();
                if (fos != null) fos.close();
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }


    /**
     * 插入图片 自定义图片的宽高
     *
     * @param imgPath    插入图片的路径
     * @param imgWidth   设置图片的宽度
     * @param imgHeight  设置图片的高度
     * @param isCompress 是否按输入的宽高定义图片的尺寸,只有为true时 输入的宽度和高度才起作用<br/>
     *                   为false时输入的宽高不起作用,按输入图片的默认尺寸
     * @return
     * @throws Exception
     */
    private static Image insertImage(String imgPath, int imgWidth, int imgHeight, boolean isCompress) throws Exception {
        File fileimage = new File(imgPath);
        Image src = ImageIO.read(fileimage);
        if (isCompress) {
            Image image = src.getScaledInstance(imgWidth, imgHeight, Image.SCALE_SMOOTH);
            return image;
        }
        return src;
    }

    /**
     * 生成
     */
    public static boolean generateOrder(String orderPath, BatOrder orderParam, HospitalInformation hospitalInformation, boolean isCompress, int imgWidth, int imgHeidht) {
        if (null == orderParam)
            return false;
        String picPath = orderPath;
        int startHeight = 0;  //表格的起始高度
        int startWidth = 0;   //表格的起始宽度
        try {
            image = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();

            //以运单号为名称创建存放订单的目录
            File mk = new File(picPath + orderParam.getTracking_number());
            if (mk.exists()) {
                FileUtils.deleteDirectory(mk);
            }
            FileUtils.forceMkdir(mk);

            //设置背景色为白色
            g.setColor(Color.WHITE);
            //设置颜色区域大小
            g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);

            /*
             * 绘制表格 填充内容
             * */
            //表格线条的颜色
            g.setColor(Color.BLACK);
            //边框加粗
            g.setStroke(new BasicStroke(2.0f));
            //消除文本出现锯齿现象
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            //设置虚线条
            Stroke bs = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{16, 4}, 0);
            g.setStroke(bs);

            //表格的四个边框
            g.drawLine(startWidth + 16, startHeight + 16, startWidth + 783, startHeight + 16); //上边框
            g.drawLine(startWidth + 16, startHeight + 16, startWidth + 16, startHeight + 1183); //左边框
            g.drawLine(startWidth + 16, startHeight + 1183, startWidth + 783, startHeight + 1183); //下边框
            g.drawLine(startWidth + 783, startHeight + 16, startWidth + 783, startHeight + 1183); //右边框

            //分割线左边
            //标准快递
            Font fontSfTyp = new Font("黑体", Font.BOLD, 45);//Font.BOLD(加粗)
            g.setFont(fontSfTyp);
            g.drawString("标准快递", startWidth + 78, startHeight + 120);

            Font fontSfTyp2 = new Font("黑体", Font.BOLD, 16);//Font.BOLD(加粗)
            g.setFont(fontSfTyp2);
            g.drawString("时间:" + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"), startWidth + 75, startHeight + 145);

            //第一行竖 分割线
            g.drawLine(startWidth + 336, startHeight + 16, startWidth + 336, startHeight + 176); //右边框

            //分割线右边
            //生成code128a 条码
            SFBarCodeGenerateUtil.generateBarCode128_a(orderParam.getTracking_number(),          //运单号
                    picPath + orderParam.getTracking_number() + ".png",           //图片名称
                    440,                                   //图片宽度
                    100);
            //导入条码图片
            Image sfBarImg = insertImage(picPath + orderParam.getTracking_number() + ".png", 0, 0, false);
            g.drawImage(sfBarImg, startWidth + 340, startHeight + 32, null);
            g.drawString(orderParam.getTracking_number(), startWidth + 500, startHeight + 150);

            //绘制表格内容 第1行
            g.drawLine(startWidth + 16, startHeight + 176, startWidth + 783, startHeight + 176);

            //绘制表格内容 第2行
            g.drawLine(startWidth + 16, startHeight + 248, startWidth + 783, startHeight + 248);
            g.setFont(fontSfTyp);
            g.drawString(orderParam.getFilePath(), startWidth + 100, startHeight + 220);

            Font fontSfTyp3 = new Font("黑体", Font.BOLD, 28);
            g.setFont(fontSfTyp3);
            //收:
            g.drawString("收:" + orderParam.getAddressName() + " " + orderParam.getAddressPhone(), startWidth + 18, startHeight + 280);
            if (orderParam.getAddress().length() > 22) {
                g.drawString("   " + orderParam.getAddress().substring(0, 22), startWidth + 18, startHeight + 310);
                g.drawString("   " + orderParam.getAddress().substring(22, orderParam.getAddress().length()), startWidth + 18, startHeight + 340);
            } else {
                g.drawString("   " + orderParam.getAddress().substring(0, orderParam.getAddress().length()), startWidth + 18, startHeight + 310);
            }

            //绘制表格内容 第3行
            g.drawLine(startWidth + 16, startHeight + 408, startWidth + 783, startHeight + 408);
            //寄
            Font fontSfTyp4 = new Font("黑体", Font.PLAIN, 20);//Font.PLAIN(普通)
            g.setFont(fontSfTyp4);

            //绘制表格内容 第4行
            g.drawLine(startWidth + 16, startHeight + 472, startWidth + 783, startHeight + 472);
            g.drawString("寄:" + hospitalInformation.getHospitalName() + " " + hospitalInformation.getPhoneNumber(), startWidth + 18, startHeight + 430);
            String address = hospitalInformation.getProvince() + hospitalInformation.getCity() + hospitalInformation.getCounty() + hospitalInformation.getArea();
            if (address.length() > 30) {
                g.drawString("   " + address.substring(0, 30), startWidth + 18, startHeight + 450);
                g.drawString("   " + address.substring(30, address.length()), startWidth + 18, startHeight + 470);
            } else {
                g.drawString("   " + address.substring(0, address.length()), startWidth + 18, startHeight + 450);
            }
            
            g.drawString("付款方式:", startWidth + 18, startHeight + 500);
            g.drawString("计费重量(KG):", startWidth + 18, startHeight + 530);
            g.drawString("保价金额:", startWidth + 18, startHeight + 560);

            //第5行竖 分割线
            g.drawLine(startWidth + 464, startHeight + 472, startWidth + 464, startHeight + 583); //右边框

            g.drawString("收件人\\代收人:", startWidth + 468, startHeight + 495);
            g.drawString("签收时间 :   年   月   日   时", startWidth + 468, startHeight + 530);
            Font fontSfTyp5 = new Font("黑体", Font.PLAIN, 16);//Font.PLAIN(普通)
            g.setFont(fontSfTyp5);
            g.drawString("快件送达收货人地址:经收件人或收件人允", startWidth + 468, startHeight + 560);
            g.drawString("许的代收人签字,视为送达", startWidth + 468, startHeight + 580);

            //绘制表格内容 第5行
            g.drawLine(startWidth + 16, startHeight + 583, startWidth + 783, startHeight + 583);

            g.setFont(fontSfTyp4);
            g.drawString("件数: 重量(KG)", startWidth + 18, startHeight + 600);
            g.drawString("配货信息:物品", startWidth + 18, startHeight + 620);

            //绘制表格内容 第5行
            g.drawLine(startWidth + 16, startHeight + 719, startWidth + 783, startHeight + 719);

            g.drawString("配货信息:物品", startWidth + 18, startHeight + 620);

            g.drawLine(startWidth + 16, startHeight + 735, startWidth + 783, startHeight + 735);

            //分割线右边
            //生成code128a 条码
            SFBarCodeGenerateUtil.generateBarCode128_a(orderParam.getTracking_number(),          //运单号
                    picPath + orderParam.getTracking_number() + ".png",           //图片名称
                    440,                                   //图片宽度
                    60);
            //导入条码图片
            Image sfBarImg2 = insertImage(picPath + orderParam.getTracking_number() + ".png", 0, 0, false);
            g.drawImage(sfBarImg2, startWidth + 20, startHeight + 750, null);
            g.drawString(orderParam.getTracking_number(), startWidth + 180, startHeight + 830);

            //插入Logo
            //从springboot资源目录获取获取logo图片地址:
            String logoPath = ResourceUtils.getFile("classpath:static/pic/ems_logo.png").getPath();
            Image sublogoImg = insertImage(logoPath, LOGO_WIDTH, LOGO_HEIGHT, true);
            g.drawImage(sublogoImg, startWidth + 480, startHeight + 750, null);

            g.drawLine(startWidth + 16, startHeight + 855, startWidth + 783, startHeight + 855);

            g.drawString("收:" + orderParam.getAddressName() + " " + orderParam.getAddressPhone(), startWidth + 18, startHeight + 880);
            if (orderParam.getAddress().length() > 20) {
                g.drawString("   " + orderParam.getAddress().substring(0, 20), startWidth + 18, startHeight + 900);
                g.drawString("   " + orderParam.getAddress().substring(20, orderParam.getAddress().length()), startWidth + 18, startHeight + 920);
            } else {
                g.drawString("   " + orderParam.getAddress().substring(0, orderParam.getAddress().length()), startWidth + 18, startHeight + 900);
            }
            //竖线
            g.drawLine(startWidth + 464, startHeight + 855, startWidth + 464, startHeight + 991);

            g.drawString("寄:" + hospitalInformation.getHospitalName(), startWidth + 468, startHeight + 880);
            g.drawString("   " + hospitalInformation.getPhoneNumber(), startWidth + 468, startHeight + 900);
            if (address.length() > 14) {
                g.drawString("   " + address.substring(0, 14), startWidth + 468, startHeight + 920);
                g.drawString("   " + address.substring(14, address.length()), startWidth + 468, startHeight + 940);
            } else {
                g.drawString("   " + address.substring(0, address.length()), startWidth + 468, startHeight + 920);
            }

            g.drawLine(startWidth + 16, startHeight + 991, startWidth + 783, startHeight + 991);

            g.drawString("备注:", startWidth + 18, startHeight + 1020);

            g.drawLine(startWidth + 16, startHeight + 1127, startWidth + 783, startHeight + 1127);

            g.drawString("网址:www.ems.com.cn   客服电话:11183", startWidth + 45, startHeight + 1160);

            g.drawLine(startWidth + 559, startHeight + 1127, startWidth + 559, startHeight + 1183);

            g.drawString("L", startWidth + 750, startHeight + 1170);

            g.dispose();
            File sfbarFile = new File(picPath + orderParam.getId() + ".jpg");
            if (sfbarFile.exists() && sfbarFile.isFile()) {
                sfbarFile.delete();
            }
            //生成订单图片
            createImage(picPath + orderParam.getId() + ".jpg");
            if (isCompress) {
                compressImg(picPath + orderParam.getId() + ".jpg", imgWidth, imgHeidht);
            }
            log.info("订单生成成功. " + picPath + orderParam.getId() + ".jpg");
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 水平翻转图像 顺时针旋转90度、左右翻转
     *
     * @param bi 目标图像
     * @return
     */
    private static BufferedImage rotate90DX(BufferedImage bi) {
        int width = bi.getWidth();
        int height = bi.getHeight();

        BufferedImage biFlip = new BufferedImage(height, width, bi.getType());

        for (int i = 0; i < width; i++)
            for (int j = 0; j < height; j++)
                biFlip.setRGB(height - 1 - j, width - 1 - i, bi.getRGB(i, j));

        return biFlip;
    }


    /**
     * 水平翻转图像 逆时针旋转90度、左右翻转
     *
     * @param bi 目标图像
     * @return
     */
    private static BufferedImage rotate90SX(BufferedImage bi) {
        int width = bi.getWidth();
        int height = bi.getHeight();

        BufferedImage biFlip = new BufferedImage(height, width, bi.getType());

        for (int i = 0; i < width; i++)
            for (int j = 0; j < height; j++)
                biFlip.setRGB(j, i, bi.getRGB(i, j));

        return biFlip;
    }

    /**
     * 根据规定尺寸压缩图片
     * @param imgPath 图片路径
     * @param width   图片宽度
     * @param height  图片高度
     */
    private static void compressImg(String imgPath, int width, int height) {
        /**
         * 设置条码图片的尺寸
         * */
        BufferedInputStream bis = null;
        BufferedOutputStream out = null;
        FileOutputStream fis = null;
        try {
            File sfFile = new File(imgPath);
            if (sfFile.isFile() && sfFile.exists()) {
                //读取图片
                bis = new BufferedInputStream(new FileInputStream(imgPath));
                //转换成图片对象
                Image bi = ImageIO.read(bis).getScaledInstance(width, height, Image.SCALE_SMOOTH);
                //构建图片流 设置图片宽和高
                BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                //绘制改变尺寸后的图
                tag.getGraphics().drawImage(bi, 0, 0, width, height, null);
                //保存图片
                fis = new FileOutputStream(imgPath);
                JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(fis);
                JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(tag);
                //设置压缩图片质量
                jep.setQuality(3f, true);
                encoder.encode(tag, jep);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fis != null) fis.close();
                if (out != null) out.close();
                if (bis != null) bis.close();
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }

    //创建目录
    private static boolean createDir(String destDirName) {
        try {
            File file = new File(destDirName);
            if (destDirName.endsWith(File.separator))
                destDirName += File.separator;
            if (file.mkdir())
                return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    //删除目录
    public static boolean removeDir(File dir) {
        File[] files = dir.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                return removeDir(file);
            } else {
                return file.delete();
            }
        }
        return dir.delete();
    }

    //删除目录
    public static boolean deleteMk(String dirPath) {
        File dirFile = new File(dirPath);
        if (dirFile.isDirectory()) {
            File[] files = dirFile.listFiles();
            for (int i = 0; i < files.length; i++) {
                files[i].delete();
            }
        }
        return dirFile.delete();
    }

    /**
     * 把文件转换为byte[]
     *
     * @param filePath 文件路径
     */
    public static byte[] getFileBytes(String filePath) {
        byte[] buffer = null;
        FileInputStream fis = null;
        ByteArrayOutputStream bos = null;
        try {
            File file = new File(filePath);
            fis = new FileInputStream(file);
            bos = new ByteArrayOutputStream(1000);
            byte[] b = new byte[1000];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return buffer;
    }

    
}

测试打印面单图片图片:

public static void main(String[] args) {
    BatOrder param = new BatOrder();

    param.setTracking_number("883987638272");
    param.setTracking_number("883987638272");
    param.setFilePath("310-新A-沙区02-沙区揽投-*");
    param.setAddressName("春哥");
    param.setAddressPhone("15261800000");
    param.setAddress("重庆市重庆市南岸区南岸区测试大道重庆交通大学测试小区1栋00单元00");

    HospitalInformation hospitalInformation = new HospitalInformation();
    hospitalInformation.setHospitalName("江苏大学");
    hospitalInformation.setPhoneNumber("025-91918230");
    hospitalInformation.setProvince("江苏");
    hospitalInformation.setCity("南京市市");
    hospitalInformation.setCounty("栖霞区区");
    hospitalInformation.setArea("南京仙林大学城199号");

    EMSGenerateUtil.generateOrder(
            "D:\\12345\\",
            param,
            hospitalInformation,
            false,
            600,
            400);
}

打印效果:

遇到的问题以及注意事项:

问题1、

上面使用Java Graphics2D 代码可能会遇到一个问题:

解决办法:该原因是(JPEGCodec类)在JDK1.7之后移除,使用 JDK1.8 打包时会报错

在当前工程的pom.xml文件中添加

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <!--改成自己本机maven版本号--->
    <version>3.8.1</version>
    <configuration>
        <source>${java.version}</source>
        <target>${java.version}</target>
        <encoding>${project.build.sourceEncoding}</encoding>
        <compilerArguments>
            <verbose/>
            <bootclasspath>${java.home}/lib/rt.jar;${java.home}/lib/jce.jar</bootclasspath>
        </compilerArguments>
    </configuration>
</plugin>

注意2、

借鉴了别人的代码,发现一个问题,关于条形码的生成需要注意:邮政使用的是code128 A码,好像顺丰使用的是code128 C码。是不一样的。

code128编码 有3套编码:A码,B码,C码。

这里A码,我们采用的 zxing 技术

代码如下

/**
 * 快递单号 wayBillNo
 *
 * @param generatePathName 指定文件名
 * @param wayBillNo        写入的内容
 * @param width            尺寸宽
 * @param height           尺寸高
 */
public static void generateBarCode128_a(String wayBillNo, String generatePathName, int width, int height) {
    Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
    // 字符集
    hints.put(EncodeHintType.CHARACTER_SET, "GBK");// Constant.CHARACTER);
    // 容错质量
    hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
    try {
        // 尺寸
        BitMatrix bitMatrix = new MultiFormatWriter().encode(wayBillNo, BarcodeFormat.CODE_128, width, height, hints);
        BufferedOutputStream buffer = null;
        buffer = new BufferedOutputStream(new FileOutputStream(generatePathName));
        // ok
        MatrixToImageWriter.writeToStream(bitMatrix, "png", new FileOutputStream(new File(generatePathName)));
        buffer.close();
    } catch (WriterException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

到这里邮政的EMS面单图片其实就可以了,你可以直接选择打印图片。

因为兼容以前的代码,这里我们还需要面单图片转成一个PDF文件,方便前端打印,原来我们前端就是获取PDF文件打印的,减少前端前端代码的改动量。

三、面单图片生成PDF文件

我们使用Itext技术,这里我们封装成了一个工具类

package com.chunge.hospital.util;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.PdfWriter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class PDFUtil {
    public static String createPDF(String pdfPath, String imagePath)
            throws IOException, DocumentException {
        Document doc = new Document(new Rectangle(283.0F, 425.0F));
        PdfWriter.getInstance(doc, new FileOutputStream(new File(pdfPath)));
        doc.open();
        Image image = Image.getInstance(imagePath);
        image.setAlignment(2);
        image.setBorder(0);
        //image.setBorderWidth(2.0F);
        //image.setBorderColor(BaseColor.RED);
        float scalePercentage = (100 / 300f) * 100.0f;
        image.setAbsolutePosition(0, 0);
        //image.scalePercent(26f,31f);
        //image.scalePercent(26f,31f);
        image.scaleAbsolute(283.0f, 425.0f); // 将图像缩放到绝对宽度和绝对高度。
        image.setAlignment(Element.ALIGN_CENTER);
        doc.add(image);
        doc.close();
        return pdfPath;
    }

    public static void main(String[] args) {
        try {
            createPDF("C:\\bat\\yunda\\e9eae660d94d42548a675442ce1f502d\\2021-08-01\\1.pdf", "C:\\bat\\yunda\\e9eae660d94d42548a675442ce1f502d\\2021-08-01\\0020200707151336201.jpg");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}

难点:注意就是图片的位置和缩放那一块,调试了很久;

生成的PDF效果:

四、页面获取PDF文件打印


<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
    <th:block th:include="include :: header('打印')" />
    <th:block th:include="include :: datetimepicker-css" />
    <script type="text/javascript">
       function print(){
          var printIframe = document.getElementById("Iframe");
          printIframe.contentWindow.print();
       }
    </script>
</head>
<body class="white-bg">
    <div style="padding:3px 5px;"><input style="border:0px;width:40px;height:20px;"  onclick="print();" type="button" value="打印" /></div>
    <iframe id="Iframe"  th:src="@{'/hospital/advance_batorder/loadPdf?id='+${id}}"  frameborder="0" style="width: 100%; height: 100%" >
    </iframe>
    
</body>
</html>

打印效果:

到此,邮政EMS面单打印功能实现。码字不易。有问题私聊吧

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值