最近搞定了一个比较头疼的事情,邮政快递面单打印的需求。
需求:系统需要支持邮政下单、打印快递面单。
坑爹之处:邮政就是牛叉任性,其他家快递都可以提供面单查询接口,直接返回面单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面单打印功能实现。码字不易。有问题私聊吧