Excel的模板导出+背景水印

1. 模板导出

当遇到比较复杂的表的时候,可以第一时间想到模板,当然简单的表也是可以很快的生成,可以简化开发的效率,不必我们在一个一个画Cell,并且需要修改各种样式,而利用模板就可以制造好模板并且可以给上对应的样式。

前几天给我了个需求,需要改造原有的一个Excel导出的功能,那个表比较复杂,一共有三个Sheet页大致的导出样式就是如此吧

Excel的模板导出+背景水印
Excel的模板导出+背景水印
Excel的模板导出+背景水印

如果要做这样的表,一个一个画是真的难顶,所有让我改造的时候我先看了下他原有的逻辑,有一点难懂,我就想着直接用模板进行改造,于是我就想了之前使用过的 EasyPIO

官方文档:

具体的使用直接官方文档吧,这个照猫画虎,照葫芦画瓢就OK,但是这个EasyPOI里面也会有很多的坑,使用需要小心。为了单纯的整这个模板的使用,我就花费了一天半的时间,主要是中间出现了很多问题,比如它里面要么纵向循环要么横向循环,但是上面的Sheet2和Sheet3都是横向加纵向,最后妥协了。后面导出样式也有问题,然后就逐步的上调版本。使用中间确实出现了不少的插曲,但是使用起来还是挺不错的

1.1 使用案例

<!-- 我使用的依赖 -->
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-spring-boot-starter</artifactId>
    <version>4.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.1</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.1</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.1.1</version>
</dependency>

具体按照文档走

1. 配置模板

Excel的模板导出+背景水印

2. 读取模板进行填充数据

    @ApiOperation("报价界面增加模版下载功能")
    @GetMapping("/generateOfferTemplate")
    public void generateOfferTemplate(@RequestParam Long projectId,@RequestParam(required = false) Long supplierId, HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.info("报价界面增加模版下载功能 {} ",projectId);
        ProjectVO projectVO = projectService.getById(projectId);
        if (projectVO == null) {
            throw new VisibleException("项目不存在");
        }
        if(supplierId == null) {
            supplierId = UserContainer.getMerchantInfo().getAuthMerchantList().get(0).getMerchantId();
        }
        // 判断这个供应商是否审核通过
        ProjectSupplierDetailVO supplierDetailVO = projectSupplierDetailService.get(new Q().eq("project_id", projectId).eq("supplier_id",supplierId));
        if(supplierDetailVO == null){
            throw new VisibleException("您还没有报名");
        }
        if(supplierDetailVO.getEnrollStatus() == 4){    // 4报名驳回
            throw new VisibleException("您的报名已被驳回");
        }
        
        // 读取模板
        TemplateExportParams params = new TemplateExportParams("excel/template/统一询价单模板.xlsx");
        // 默认读取所有的模板
        params.setSheetNum(new Integer[]{0});
        Map<String, Object> map = new HashMap<>();
        // 封装数据
        packageOfferTemplateData(projectVO,supplierDetailVO,map);
        // 填充数据
        Workbook workbook = ExcelExportUtil.exportExcel(params, map);
        // 样式更改
        buildStyle(projectVO, workbook);
        // 添加水印
        addWatermarkByPic(workbook);
        WebUtils.setXlsxAttachmentHeader(request, response, String.format("%s_询价单.xlsx", projectVO.getName()));

        workbook.write(response.getOutputStream());
    }



 	/**
     * 封装报价界面增加模版下载功能数据
     * @param projectVO
     * @param map
     */
    private void packageOfferTemplateData(ProjectVO projectVO, ProjectSupplierDetailVO supplierDetailVO,Map<String, Object> map) {
        map.put("purchaseCompanyName",projectVO.getPurchaserMerchantName());
        map.put("projectName",projectVO.getName());
        map.put("purchasePersonName",projectVO.getPurchaserName());
        map.put("purchaseContact",projectVO.getPurchaserContact());
        // 询价日期:企业招标取招标文件生效日期;询比价取立项生效日期
        map.put("date",formDate(projectVO.getCreateTime()));
        // TODO 取平台立项的期望交货期,如立项不写不带内容
        map.put("arrivalDate","");
        // 企业招标取平台招标文件的报价截止日期;询比价取邀请招标的立项中的报价截止时间
        map.put("expirationDate",formDate(projectVO.getQuotationDate()));
        //取平台报价覆盖率字段;+报价是否含运费+平台立项中的“报名要求说明”字段
        StringBuilder otherRequire = new StringBuilder("投标覆盖率:");
        String quotationCoverage = projectVO.getQuotationCoverage();
        Map quotationCoverageMap = JSON.parseObject(quotationCoverage, Map.class);
        if("percent".equals(quotationCoverageMap.get("type"))){   // 百分比
            otherRequire.append(quotationCoverageMap.get("val")).append("%");
        }else if("numAtList".equals(quotationCoverageMap.get("type")) ||quotationCoverageMap.get("type") == null){
            otherRequire.append("至少").append(quotationCoverageMap.get("val")).append("项;  ");
        }
        otherRequire.append(ProjectConstant.YES.equals(projectVO.getIsIncludeFreight()) ? "报价含运费;  " : "报价不含运费;  ");
        otherRequire.append(excludeNull(projectVO.getDescription()));
        map.put("otherRequire",otherRequire.toString());


        map.put("supplierName",supplierDetailVO.getSupplierName());
        map.put("supplierPersonName",supplierDetailVO.getEnrollContact());
        map.put("supplierContact",supplierDetailVO.getEnrollPhone());
        // 报价日期:取平台的点击生成报价单时间
        List<ProjectQuotationVO> projectQuotationVOList = projectQuotationService.list(new Q().eq("project_id", projectVO.getId()).eq("supplier_id", supplierDetailVO.getSupplierId()));
        if(CollectionUtils.isEmpty(projectQuotationVOList)){
            map.put("supplierDate","");
        }else {
            Date createTime = projectQuotationVOList.get(0).getCreateTime();
            map.put("supplierDate",formDate(createTime));
        }

        // 结算方式:如果供应商没有修改采购方结算方式带出,如果供应商修改了带供应商的:电汇/承兑+账期   结
        StringBuilder paymentType = new StringBuilder("");
        String payType = ProjectConstant.PayType.BILLING_PERIOD.equals(projectVO.getPayType()) ? "账期" : "分阶段";
        Map accountPeriod = JSON.parseObject(projectVO.getAccountPeriod(), Map.class);
        Object val = accountPeriod.get("val");
        paymentType.append(ProjectConstant.PayMethod.WIRE_TRANSFER.equals(projectVO.getPayMethod()) ? "电汇 " : "承兑 ")
                .append(payType + val + (projectVO.getPayType().equals(1) ? "天" : ""));
        map.put("paymentType",paymentType.toString());
        map.put("indate","");
        map.put("transportMode","汽运");

        BigDecimal offerMoney = BigDecimal.ZERO;

        List<Map<String, Object>> valList = new ArrayList<>();
        List<ProjectItemVO> projectItemVOList = projectItemService.list(new Q().eq("project_id", projectVO.getId()));
        Map<Long, List<ProjectQuotationItemVO>> itmeMap = projectQuotationItemService.list(new Q().eq("project_id", projectVO.getId())
                .eq("supplier_id", supplierDetailVO.getSupplierId()).eq("quotation_round", ProjectConstant.YES))
                .stream().collect(Collectors.groupingBy(ProjectQuotationItemVO::getProjectItemId));
        int index = 0;
        for (ProjectItemVO projectItemVO : projectItemVOList) {
            index++;
            Map<String, Object> tempMap = new HashMap<>();
            tempMap.put("no",index+"");
            tempMap.put("purchaseApplyCode",projectItemVO.getPurchaseApplyCode());
            tempMap.put("purchasePlanCode",projectItemVO.getPurchasePlanCode());
            tempMap.put("applyOrg",projectItemVO.getApplyOrg());
            tempMap.put("applyDep",projectItemVO.getApplyDep());
            tempMap.put("applier",projectItemVO.getApplier());
            tempMap.put("productCode",projectItemVO.getProductCode());
            tempMap.put("productName",projectItemVO.getProductNum());
            tempMap.put("productSpecs",projectItemVO.getProductSpecs());
            tempMap.put("productUnit",projectItemVO.getProductUnit());
            tempMap.put("productNum",projectItemVO.getProductNum());
            tempMap.put("remark",projectItemVO.getRemark());

            tempMap.put("priceWttax","");
            tempMap.put("priceTotal","");
            tempMap.put("productBrand","");
            tempMap.put("standardModel","");
            tempMap.put("conformityCertificate","");
            tempMap.put("deliverDate","");
            tempMap.put("exp","");
            tempMap.put("supplierRemark","");
            valList.add(tempMap);
            if(itmeMap.size() == 0){
                continue;
            }
            List<ProjectQuotationItemVO> projectQuotationItemVOS = itmeMap.get(projectItemVO.getId());
            if(projectQuotationItemVOS.size() == 0){
                continue;
            }
            ProjectQuotationItemVO projectQuotationItemVO = projectQuotationItemVOS.get(0);

            offerMoney = offerMoney.add(projectQuotationItemVO.getProductPriceTotal());
            tempMap.put("priceWttax",projectQuotationItemVO.getProductPriceWttax());
            tempMap.put("priceTotal",projectQuotationItemVO.getProductPriceTotal());
            tempMap.put("productBrand",projectQuotationItemVO.getProductBrand());
            // 现在平台没有该字段,待平台添加字段后自动带出
            tempMap.put("standardModel","");
            // 现在平台没有该字段,待平台添加字段后自动带出
            tempMap.put("conformityCertificate","");
            tempMap.put("deliverDate",projectQuotationItemVO.getDeliverDate());
            tempMap.put("exp",projectQuotationItemVO.getExp());
            tempMap.put("supplierRemark",projectQuotationItemVO.getRemark());
            // valList.add(tempMap);
        }

        String maxMoney = ConvertMaxMoney.toChinese(offerMoney.toString());
        map.put("minMoney",formatBigDecimal2(offerMoney));
        map.put("maxMoney",maxMoney);
        map.put("taxRate",formatBigDecimal2(projectVO.getTaxRate()) + "%");
        map.put("valList",valList);
    }

中间还有其他的步骤

3. 写出

1.2 模板文件损坏问题

会有这样的一种情况,我们会把模板放在resouces文件夹下,在代码中读取的文件最终还是编译后的文件,但是 xlsx 和 xls excel文件在这个过程中会被损坏,导致数据填充出现问题。需要在pom文件中添加一下代码

	<!-- 解决项目编译破坏 excel 文件 -->
	<build>
		<finalName>bidding-web</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-resources-plugin</artifactId>
				<configuration>
					<encoding>UTF-8</encoding>
					<nonFilteredFileExtensions>
						<nonFilteredFileExtension>xlsx</nonFilteredFileExtension>
						<nonFilteredFileExtension>xls</nonFilteredFileExtension>
					</nonFilteredFileExtensions>
				</configuration>
			</plugin>
		</plugins>
	</build>

2. 添加Excel水印

之前听过在PDF上和Word上添加水印,但是没有听过在Excel上添加水印。

在网上搜索了下,资料不太多,而且很多不能满足需求。网上大部分的水印都是类似于遮罩的东西,图片或者文字会挡住Excel,影响到正常Excel的使用

我要的效果是: 导出模板,下载模板的人可以对此进行修改,那就代表着水印就相当于是背景图片,不能影响正常Excel表格的修改。在这个中途看到阿里的EasyExcel可以进行实现。但是我这种是已经填充完的数据不能在使用对应的工具类,只能就罢。最终我找到了一篇博客里面记录的他实现的效果。其实这篇博客之前也有看到过,也尝试了,因为我使用的 poi的版本是 4.1.1,因为版本太低我的模板导出样式会出现问题,所以不能降低版本,导致里面的有的对象没有对应方法

sheet.getCTWorksheet().addNewPicture().setId(pr.getId());

就是上面的 setId() 这个方法,找解决方法看是需要该poi源码在编译成jar包,当时就想着鱼和熊掌不可兼得,只能捡西瓜,丢芝麻。最终最终,我从其他人的实例中找到了问题的答案,想着在这也试一试,于是成功了,原来只需要引入一个依赖就OK了。

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>ooxml-schemas</artifactId>
    <version>1.0</version>
</dependency>

先看下最终的实现效果

文字水印
Excel的模板导出+背景水印

图片水印
Excel的模板导出+背景水印

代码实现

    /**
     * 添加水印(图片)
     * @param workbook
     */
    private void addWatermarkByPic(Workbook workbook) {
        try {
            InputStream inputStream = ProjectQuotationController.class.getClassLoader().getResourceAsStream("excel/waterremark/dingdang_logo_waterremark_250.png");
            BufferedImage bufferedImage = ImageIO.read(inputStream);
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            if (null == bufferedImage) {
                throw new RuntimeException("向Excel上面打印水印,读取水印图片失败(2)。");
            }
            ImageIO.write(bufferedImage, "png", os);
            int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
            POIXMLDocumentPart poixmlDocumentPart = (POIXMLDocumentPart) workbook.getAllPictures().get(pictureIdx);
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {//获取每个Sheet表
                XSSFSheet sheet = (XSSFSheet) workbook.getSheetAt(i);
                PackagePartName ppn = poixmlDocumentPart.getPackagePart().getPartName();
                String relType = XSSFRelation.IMAGES.getRelation();
                //add relation from sheet to the picture data
                PackageRelationship pr = sheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
                //set background picture to sheet
                sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
            }

        } catch (IOException e) {
            logger.error("添加图片水印失败:{}",e);
            e.printStackTrace();
        }
    }

    /**
     * 添加水印(文字)
     * @param workbook
     */
    private void addWatermarkByText(Workbook workbook) {
        try {
            BufferedImage bufferedImage = ExcelWaterRemarkUtils.createTransparentImage(null,"叮当","excel/font/songti.ttc");//fontPath 字体文件所在路径
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            if (null == bufferedImage) {
                throw new RuntimeException("向Excel上面打印水印,读取水印图片失败(2)。");
            }
            ImageIO.write(bufferedImage, "png", os);
            int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
            POIXMLDocumentPart poixmlDocumentPart = (POIXMLDocumentPart) workbook.getAllPictures().get(pictureIdx);
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {//获取每个Sheet表
                XSSFSheet sheet = (XSSFSheet) workbook.getSheetAt(i);
                PackagePartName ppn = poixmlDocumentPart.getPackagePart().getPartName();
                String relType = XSSFRelation.IMAGES.getRelation();
                //add relation from sheet to the picture data
                PackageRelationship pr = sheet.getPackagePart().addRelationship(ppn, TargetMode.INTERNAL, relType, null);
                //set background picture to sheet
                sheet.getCTWorksheet().addNewPicture().setId(pr.getId());
            }

        } catch (IOException e) {
            logger.error("添加文字水印失败 {}",e);
            e.printStackTrace();
        }
    }

ExcelWaterRemarkUtils工具类

package com.odianyun.inquiry.web.controller.assist.utils;



import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;


public class ExcelWaterRemarkUtils {
    public static final String inFilePath = "E:\\360MoveData\\Users\\84741\\Desktop\\综合评议表模板.xlsx";

    // 宽度
    private static final int WIDTH = 600;
    // 高度
    private static final int HEIGHT = 200;
    // 水印透明度
    private static float alpha = 0.2f;
    // 水印横向位置
    private static int positionWidth = 100;
    // 水印纵向位置
    private static int positionHeight = 40;
    // 水印文字字体
    private static Font font = new Font("宋体", Font.BOLD ,70);
    // 水印文字颜色
    private static Color color = new Color(230, 230, 230);

    /*
     * 为Excel打上水印工具函数 请自行确保参数值,以保证水印图片之间不会覆盖。 在计算水印的位置的时候,并没有考虑到单元格合并的情况,请注意
     *
     * @param wb
     *            Excel Workbook
     * @param sheet
     *            需要打水印的Excel
     * @param waterRemarkPath
     *            水印地址,classPath,目前只支持png格式的图片,
     *            因为非png格式的图片打到Excel上后可能会有图片变红的问题,且不容易做出透明效果。
     *            同时请注意传入的地址格式,应该为类似:"\\excelTemplate\\test.png"
     * @param startXCol
     *            水印起始列
     * @param startYRow
     *            水印起始行
     * @param betweenXCol
     *            水印横向之间间隔多少列
     * @param betweenYRow
     *            水印纵向之间间隔多少行
     * @param XCount
     *            横向共有水印多少个
     * @param YCount
     *            纵向共有水印多少个
     * @param waterRemarkWidth
     *            水印图片宽度为多少列
     * @param waterRemarkHeight
     *            水印图片高度为多少行
     * @throws IOException
     */
    public static void putWaterRemarkToExcel(Workbook wb, Sheet sheet, BufferedImage bufferImg, int startXCol,
                                             int startYRow, int betweenXCol, int betweenYRow, int XCount, int YCount, int waterRemarkWidth,
                                             int waterRemarkHeight) throws IOException {
        // 加载图片
        ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
        if (null == bufferImg) {
            throw new RuntimeException("向Excel上面打印水印,读取水印图片失败(2)。");
        }
        ImageIO.write(bufferImg, "png", byteArrayOut);
        // 开始打水印
        Drawing drawing = sheet.createDrawingPatriarch();
        // 按照共需打印多少行水印进行循环
        for (int yCount = 0; yCount < YCount; yCount++) {
            // 按照每行需要打印多少个水印进行循环
            for (int xCount = 0; xCount < XCount; xCount++) {
                // 创建水印图片位置
                int xIndexInteger = startXCol + (xCount * waterRemarkWidth) + (xCount * betweenXCol);
                int yIndexInteger = startYRow + (yCount * waterRemarkHeight) + (yCount * betweenYRow);
                /*
                 * 参数定义: 第一个参数是(x轴的开始节点); 第二个参数是(是y轴的开始节点); 第三个参数是(是x轴的结束节点);
                 * 第四个参数是(是y轴的结束节点); 第五个参数是(是从Excel的第几列开始插入图片,从0开始计数);
                 * 第六个参数是(是从excel的第几行开始插入图片,从0开始计数); 第七个参数是(图片宽度,共多少列);
                 * 第8个参数是(图片高度,共多少行);
                 */
                ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, xIndexInteger,
                        yIndexInteger, xIndexInteger + waterRemarkWidth, yIndexInteger + waterRemarkHeight);
                Picture pic = drawing.createPicture(anchor,
                        wb.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_PNG));
                pic.resize();
            }
        }
    }

    /**
     * 创建背景透明图片
     *
     * @param os
     * @param text
     * @param fontPath
     * @throws IOException
     */
    public static BufferedImage createTransparentImage(OutputStream os, String text,String fontPath) throws IOException {
        // 创建空白图片
        BufferedImage image = new BufferedImage(
                WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
        // 获取图片画笔
        Graphics2D g = image.createGraphics();
        // 设置背景透明度
        image = g.getDeviceConfiguration().createCompatibleImage(WIDTH, HEIGHT, Transparency.TRANSLUCENT);
        g.dispose();
        g = image.createGraphics();
        // 设置对线段的锯齿状边缘处理
        // g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 文字处理
        AttributedString ats = new AttributedString(text);

        // 需要注意,如果生产环境部署在docker 或者是 是linux服务器,很有可能会引起中文水印乱码 成了方块的情况,这时候你就需要自己导入字体放到项目路径.然后读取.下面是字体相关的工具类
        Font  font1 = SystemLoadFont.styleFont(fontPath,Font.BOLD,70);
        // Font  font1 = font;
        ats.addAttribute(TextAttribute.FONT, font1, 0, text.length());
        AttributedCharacterIterator iter = ats.getIterator();
        // 水印旋转
        g.rotate(Math.toRadians(-15), (double) image.getWidth() / 2, (double) image.getHeight() / 2);
        // 设置水印文字颜色
        g.setColor(color);
        // 设置水印文字Font
        g.setFont(font1);
        // alpha = 0.3f;
        // 设置水印文字透明度
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
        // 设置水印文字透明度结束
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1f));
        // 水印位置
        g.drawString(iter, positionHeight, positionWidth);
        // 释放资源
        g.dispose();
        return image;
    }


    public static void main(String[] args) {

        try {
            //读取excel文件
            Workbook wb =null;
            if (inFilePath.endsWith("xls")) {
                wb = new HSSFWorkbook(new FileInputStream(inFilePath));
            }else if (inFilePath.endsWith("xlsx")){
                wb = new XSSFWorkbook(new FileInputStream(inFilePath));
            }
            //获取excel sheet个数
            int sheets = wb.getNumberOfSheets();
            //循环sheet给每个sheet添加水印
            for (int i = 0; i < sheets; i++) {
                Sheet sheet = wb.getSheetAt(i);
                //获取excel实际所占行
                int row = sheet.getFirstRowNum() + sheet.getLastRowNum();
                //获取excel实际所占列
                Row row1 = sheet.getRow(sheet.getFirstRowNum());
                if (row1==null){
                    continue;
                }
                int cell = sheet.getRow(sheet.getFirstRowNum()).getLastCellNum() + 1;
                //根据行与列计算实际所需多少水印
                ExcelWaterRemarkUtils.putWaterRemarkToExcel(wb, sheet, null, 0, 0, 15, 15, cell / 15 + 1, row / 15 + 1, 0, 0);

                sheet.protectSheet("xxx");
            }
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            try {
                wb.write(os);
            } catch (IOException e) {
                e.printStackTrace();
            }
            wb.close();
            byte[] content = os.toByteArray();
            // Excel文件生成后存储的位置。
            File file1 = new File(inFilePath);
            OutputStream fos = null;
            try {
                fos = new FileOutputStream(file1);
                fos.write(content);
                os.close();
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}


public class SystemLoadFont {
    private static Logger logger = LoggerFactory.getLogger(SystemLoadFont.class);
    /**
     * 本地读取方法
     * @param path 文件路径
     * @param style 字体样式
     * @param fontSize 字体大小
     * @return
     */
    public static java.awt.Font styleFont(String path,int style,float fontSize) {
       
        return SystemLoadFont.loadStyleFont(path,style,fontSize);
    }
    /**
     *
     * @param fontFileName 外部字体名
     * @param style 字体样式
     * @param fontSize 字体大小
     * @return
     */
    public static Font loadStyleFont(String fontFileName, int style, float fontSize) {
        try{
            InputStream in = SystemLoadFont.class.getClassLoader().getResourceAsStream(fontFileName);
            Font dynamicFont = Font.createFont(Font.TRUETYPE_FONT, in);
            Font dynamicFontPt =  dynamicFont.deriveFont(style,fontSize);
            in.close();
            return dynamicFontPt;
        }catch(Exception e) {//异常处理
            logger.info("读取字体文件失败");
            return new java.awt.Font("宋体", Font.BOLD, 70);
        }
    }
}

需要注意,如果生产环境部署在docker 或者是 是linux服务器,很有可能会引起中文水印乱码 成了方块的情况,这时候你就需要自己导入字体放到项目路径.然后读取.下面是字体相关的工具类

可以找到本机的文字文件拷到resouces目录下进行读取。

参考文章:

https://www.cnblogs.com/binzhou75/p/13884377.html

https://www.jianshu.com/p/5ebf2217f0be

上面这两个实例做出来的效果就是类似于遮罩的那种,虽然说不是我要的效果,但是也让我明白了大致的流程

https://www.freesion.com/article/1208834155/

这个实例解决了我最终的问题

上一篇:day13_面向对象(super丶final丶package丶import)


下一篇:12 . Kubernetes之Statefulset 和 Operator