李锋镝的博客

  • 首页
  • 时间轴
  • 留言
  • 插件
  • 左邻右舍
  • 关于我
    • 关于我
    • 另一个网站
  • 知识库
  • 赞助
Destiny
自是人生长恨水长东
  1. 首页
  2. 原创
  3. 正文

使用itext和freemarker来根据Html模板生成PDF文件,加水印、印章

2019年9月5日 22366点热度 1人点赞 5条评论

功能:

实现根据freemarker模板生成对应的PDF文件;
可以指定文字、位置、页数生成指定的印章(图片),可以指定印章大小;
指定字体、字体大小、文字方向、颜色等生成文字水印
 
maven依赖:
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf-itext5</artifactId>
            <version>9.1.18</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.27-incubating</version>
        </dependency>
将html模板和数据进行匹配,然后用流的方式生成PDF文件
生成PDF工具类源码:
package com.lifengdi.file.pdf;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import com.lifengdi.config.GlobalConfig;
import com.lifengdi.config.PDFFontConfig;
import com.lifengdi.config.SystemConfig;
import com.lifengdi.file.pdf.listener.MyTextLocationListener;
import com.lifengdi.model.pdf.Location;
import com.lifengdi.model.pdf.PDFStamperConfig;
import com.lifengdi.model.pdf.PDFTempFile;
import com.lifengdi.model.pdf.PDFWatermarkConfig;
import com.lifengdi.util.GeneratedKey;
import com.lifengdi.util.MyStringUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.annotation.Resource;
import java.io.*;
import java.util.Objects;

/**
 * 生成PDF工具类
 *
 * @author 李锋镝
 * @date Create at 10:42 2019/5/9
 */
@Component
@Slf4j
public class HtmlToPDF {

    @Resource
    private SystemConfig systemConfig;

    @Resource
    private GeneratedKey generatedKey;

    /**
     * 模板和数据匹配
     *
     * @param data        数据
     * @param pdfTempFile pdfTempFile
     * @return html
     */
    public String matchDataToHtml(Object data, PDFTempFile pdfTempFile) {
        StringWriter writer = new StringWriter();
        String html;
        try {
            // FreeMarker配置
            Configuration config = new Configuration(Configuration.VERSION_2_3_25);
            config.setDefaultEncoding(GlobalConfig.DEFAULT_ENCODING);
            // 注意这里是模板所在文件夹,不是模版文件
            String parentPath, tempFileName = pdfTempFile.getTemplateFileName();
            if (MyStringUtil.isHttpUrl(pdfTempFile.getTemplateFileParentPath())) {
                parentPath = systemConfig.getLocalTempPath();
            } else {
                parentPath = pdfTempFile.getTemplateFileParentPath();
                // 将项目中的文件copy到服务器本地
                if (!parentPath.endsWith(File.separator))
                    parentPath = parentPath + File.separator;
                String localTempPath = systemConfig.getLocalTempPath();
                String target = (localTempPath.endsWith(File.separator) ? localTempPath : localTempPath + File.separator) + tempFileName;
                InputStream tempFileInputStream = new ClassPathResource(parentPath + tempFileName).getInputStream();
                FileUtils.copyInputStreamToFile(tempFileInputStream, new File(target));
            }
            log.info("模板和数据匹配,模板文件parentPath:{}", parentPath);

            config.setDirectoryForTemplateLoading(new File(systemConfig.getLocalTempPath()));
            config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
            config.setLogTemplateExceptions(false);
            // 根据模板名称 获取对应模板
            Template template = config.getTemplate(tempFileName);
            // 模板和数据的匹配
            template.process(data, writer);
            writer.flush();
            html = writer.toString();
            return html;
        } catch (Exception e) {
            log.error("PDF模板和数据匹配异常", e);
        } finally {
            try {
                writer.close();
            } catch (IOException e) {
                log.error("StringWriter close exception.", e);
            }
        }
        return null;
    }

    /**
     * 生成PDF
     *
     * @param html           html字符串
     * @param targetFileName 目标文件名
     * @return 生成文件的名称
     * @throws Exception e
     */
    public String createPDF(String html, String targetFileName) throws Exception {
        if (StringUtils.isBlank(html)) {
            return null;
        }
        targetFileName = getFileName(targetFileName);
        log.info("生成PDF,targetFileName:{}", targetFileName);
        String targetFilePath = getTargetFileTempPath(targetFileName);
        FileOutputStream outFile = new FileOutputStream(targetFilePath);
        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocumentFromString(html);
        // 解决中文支持问题
        log.info("加载字体");
        ITextFontResolver fontResolver = renderer.getFontResolver();
        fontResolver.addFont(SystemConfig.SourceHanSansCN_Regular_TTF, "SourceHanSansCN", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, null);
        fontResolver.addFont(SystemConfig.FONT_PATH_SONG, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        renderer.layout();
        renderer.createPDF(outFile);
        log.info("生成PDF,targetFileName:{},生成成功", targetFileName);
        return targetFileName;
    }

    /**
     * 渲染文件
     *
     * @param filePath           PDF文件路径
     * @param outFilePath        PDF文件路径
     * @param pdfStamperConfig   pdfStamperConfig
     * @param pdfWatermarkConfig pdfWatermarkConfig
     */
    public void renderLayer(String filePath, String outFilePath, PDFStamperConfig pdfStamperConfig, PDFWatermarkConfig pdfWatermarkConfig) {

        log.info("渲染文件,filePath:{},outFilePath:{}", filePath, outFilePath);
        InputStream inputStream = null;
        try {
            filePath = getTargetFileTempPath(filePath);
            inputStream = new FileInputStream(filePath);
            PdfReader reader = new PdfReader(inputStream);
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(getTargetFileTempPath(outFilePath)));
            if (Objects.nonNull(pdfStamperConfig) && pdfStamperConfig.getGenerate()) {
                // 印章
                log.info("添加印章,pdfStamperConfig:{}", pdfStamperConfig);
                stamper(pdfStamperConfig, reader, stamper);
            }
            if (Objects.nonNull(pdfWatermarkConfig) && pdfWatermarkConfig.getGenerate()) {
                // 水印
                log.info("添加水印,pdfWatermarkConfig:{}", pdfWatermarkConfig);
                watermark(reader, stamper, pdfWatermarkConfig);
            }
            stamper.close();
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != inputStream) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 印章
     *
     * @param pdfStamperConfig pdfStamperConfig
     * @param reader           reader
     * @param stamper          stamper
     * @throws IOException       IOException
     * @throws DocumentException DocumentException
     */
    private void stamper(PDFStamperConfig pdfStamperConfig, PdfReader reader, PdfStamper stamper) throws IOException, DocumentException {
        Image image;
        if (!MyStringUtil.isHttpUrl(pdfStamperConfig.getStamperUrl())) {
            // 将项目中的文件copy到服务器本地
            String imagePath = pdfStamperConfig.getStamperUrl();
            String imageName = imagePath.substring(imagePath.indexOf("/") + 1, imagePath.length());
            String localTempPath = systemConfig.getLocalTempPath();
            String target = (localTempPath.endsWith(File.separator) ? localTempPath : localTempPath + File.separator) + imageName;
            InputStream tempFileInputStream = new ClassPathResource(pdfStamperConfig.getStamperUrl()).getInputStream();
            FileUtils.copyInputStreamToFile(tempFileInputStream, new File(target));
            image = Image.getInstance(target);
        } else {
            image = Image.getInstance(pdfStamperConfig.getStamperUrl());
        }

        PdfReaderContentParser parser = new PdfReaderContentParser(reader);
        Document document = new Document();
        log.info("A4纸width:{},height:{}", document.getPageSize().getWidth(), document.getPageSize().getHeight());
//            // 取所在页和坐标,左下角为起点
//            float x = document.getPageSize().getWidth() - 240;
//            float y = document.getPageSize().getHeight() - 680;
        int pageSize = reader.getNumberOfPages();
        Location location = pdfStamperConfig.getLocation();
        if (Objects.nonNull(location)) {
            Integer page = location.getPage();// -1:全部,0:最后一页,1:首页
            if (Objects.nonNull(page)) {
                switch (page) {
                    case -1:
                        for (int pageNumber = 1; pageNumber <= pageSize; pageNumber++) {
                            insertImage(pdfStamperConfig, stamper, image, parser, pageSize);
                        }
                        break;
                    case 0:
                        insertImage(pdfStamperConfig, stamper, image, parser, pageSize);
                        break;
                    default:
                        if (page > 0) {
                            insertImage(pdfStamperConfig, stamper, image, parser, page);
                        }
                        break;
                }
            }

        }
    }

    /**
     * 向指定位置插入图片
     *
     * @param pdfStamperConfig pdfStamperConfig
     * @param stamper          stamper
     * @param image            image
     * @param parser           parser
     * @param pageNumber       pageNumber
     * @throws IOException       IOException
     * @throws DocumentException DocumentException
     */
    private void insertImage(PDFStamperConfig pdfStamperConfig, PdfStamper stamper, Image image, PdfReaderContentParser parser, int pageNumber)
            throws IOException, DocumentException {
        float x, y;
        Location xy = new Location();
        if (StringUtils.isNotBlank(pdfStamperConfig.getLocation().getWord())) {
            parser.processContent(pageNumber, new MyTextLocationListener(pdfStamperConfig, xy));
        } else {
            xy = pdfStamperConfig.getLocation();
        }
        if (Objects.isNull(xy)) {
            return;
        }

        if (Objects.isNull(xy.getX()) || Objects.isNull(xy.getY())) {
            return;
        }
        x = xy.getX();
        y = xy.getY();
        if (x < 0 || y < 0) {
            return;
        }
        // 读图片
        // 获取操作的页面
        PdfContentByte under = stamper.getOverContent(pageNumber);
//         根据域的大小缩放图片
//        image.scaleToFit(Objects.isNull(pdfStamperConfig.getFitWidth()) ? GlobalConfig.PDF_STAMPER_DEFAULT_FIT_WIDTH : pdfStamperConfig.getFitWidth(),
//                Objects.isNull(pdfStamperConfig.getFitHeight()) ? GlobalConfig.PDF_STAMPER_DEFAULT_FIT_HEIGHT : pdfStamperConfig.getFitHeight());
        image.scaleAbsolute(Objects.isNull(pdfStamperConfig.getFitWidth()) ? GlobalConfig.PDF_STAMPER_DEFAULT_FIT_WIDTH : pdfStamperConfig.getFitWidth(),
                Objects.isNull(pdfStamperConfig.getFitHeight()) ? GlobalConfig.PDF_STAMPER_DEFAULT_FIT_HEIGHT : pdfStamperConfig.getFitHeight());
        // 添加图片
        image.setAbsolutePosition(x, y);
        under.addImage(image);
    }

    /**
     * 水印
     *
     * @param reader             reader
     * @param stamper            stamper
     * @param pdfWatermarkConfig pdfWatermarkConfig
     */
    private void watermark(PdfReader reader, PdfStamper stamper, PDFWatermarkConfig pdfWatermarkConfig) {
        if (Objects.isNull(pdfWatermarkConfig) || !pdfWatermarkConfig.getGenerate()) {
            return;
        }
        PdfContentByte under;
        // 字体
        BaseFont font = PDFFontConfig.FONT_MAP.get(PDFFontConfig.SIM_SUN);
        String fontFamily = pdfWatermarkConfig.getFontFamily();
        if (StringUtils.isNotBlank(fontFamily) && PDFFontConfig.FONT_MAP.containsKey(fontFamily)) {
            font = PDFFontConfig.FONT_MAP.get(fontFamily);
        }
        // 原pdf文件的总页数
        int pageSize = reader.getNumberOfPages();
        PdfGState gs = new PdfGState();
        // 设置填充字体不透明度为0.1f
        gs.setFillOpacity(0.1f);
        Document document = new Document();
        float documentWidth = document.getPageSize().getWidth(), documentHeight = document.getPageSize().getHeight();
        Location location = pdfWatermarkConfig.getLocation();
        if (Objects.isNull(location)) {
            location = new Location();
        }
        final float xStart = 0, yStart = 0,
                xInterval = Objects.nonNull(location.getXInterval()) ? location.getXInterval() : GlobalConfig.DEFAULT_X_INTERVAL,
                yInterval = Objects.nonNull(location.getYInterval()) ? location.getYInterval() : GlobalConfig.DEFAULT_Y_INTERVAL,
                rotation = 45,
                fontSize = Objects.isNull(pdfWatermarkConfig.getFontSize()) ? GlobalConfig.DEFAULT_FONT_SIZE : pdfWatermarkConfig.getFontSize();

        String watermarkWord = pdfWatermarkConfig.getWatermarkWord();
        int red = -1, green = -1, blue = -1;
        String[] colorArray = pdfWatermarkConfig.getWatermarkColor().split(",");
        if (colorArray.length >= 3) {
            red = Integer.parseInt(colorArray[0]);
            green = Integer.parseInt(colorArray[1]);
            blue = Integer.parseInt(colorArray[2]);
        }
        for (int i = 1; i <= pageSize; i++) {
            // 水印在之前文本下
            if (Objects.nonNull(location.getOverContent()) && location.getOverContent()) {
                under = stamper.getOverContent(i);
            } else {
                under = stamper.getUnderContent(i);
            }
            under.beginText();
            // 文字水印 颜色
            if (red >= 0) {
                under.setColorFill(new BaseColor(red, green, blue));
            } else {
                under.setColorFill(BaseColor.GRAY);
            }
            // 文字水印 字体及字号
            under.setFontAndSize(font, fontSize);
            under.setGState(gs);
            // 文字水印 起始位置
            under.setTextMatrix(xStart, yStart);

            if (StringUtils.isNotBlank(watermarkWord)) {
                for (float x = xStart; x <= documentWidth + xInterval; x += xInterval) {
                    for (float y = yStart; y <= documentHeight + yInterval; y += yInterval) {
                        under.showTextAligned(Element.ALIGN_CENTER, watermarkWord, x, y, rotation);
                    }
                }
            }
            under.endText();
        }
    }

    /**
     * 获取生成的PDF文件本地临时路径
     *
     * @param targetFileName 目标文件名
     * @return 本地临时路径
     */
    public String getTargetFileTempPath(String targetFileName) {

        String localTempPath = systemConfig.getLocalTempPath();
        if (!localTempPath.endsWith(File.separator)) {
            localTempPath = localTempPath + File.separator;
        }
        return localTempPath + targetFileName;
    }

    private String getFileName(String targetFileName) {
        if (StringUtils.isBlank(targetFileName)) {
            targetFileName = generatedKey.generatorKey();
        }
        if (!StringUtils.endsWithIgnoreCase(targetFileName, GlobalConfig.PDF_SUFFIX)) {
            targetFileName = targetFileName + GlobalConfig.PDF_SUFFIX;
        }
        return targetFileName;
    }
}
获取指定文字坐标,用来在指定文字上生成印章,主要实现了RenderListener来获取指定文字的坐标。
源码:
package com.lifengdi.file.pdf.listener;

import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import com.lifengdi.config.GlobalConfig;
import com.lifengdi.model.pdf.Location;
import com.lifengdi.model.pdf.PDFStamperConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.Objects;

/**
 * @author 李锋镝
 * @date Create at 15:32 2019/5/9
 */
@Slf4j
public class MyTextLocationListener implements RenderListener {

    private String text;

    private PDFStamperConfig pdfStamperConfig;

    private Location location;

    public MyTextLocationListener(PDFStamperConfig pdfStamperConfig, Location location) {
        if (StringUtils.isNotBlank(pdfStamperConfig.getLocation().getWord())) {
            this.text = pdfStamperConfig.getLocation().getWord();
        }

        this.pdfStamperConfig = pdfStamperConfig;
        this.location = location;
    }

    @Override
    public void beginTextBlock() {
    }

    @Override
    public void renderText(TextRenderInfo renderInfo) {
        String renderInfoText = renderInfo.getText();
        if (!StringUtils.isEmpty(renderInfoText) && renderInfoText.contains(text)) {
            Rectangle2D.Float base = renderInfo.getBaseline().getBoundingRectange();
            float leftX = (float) base.getMinX();
            float leftY = (float) base.getMinY() - 1;
            float rightX = (float) base.getMaxX();
            float rightY = (float) base.getMaxY() + 1;
            Rectangle2D.Float rect = new Rectangle2D.Float(leftX, leftY, rightX - leftX, rightY - leftY);

            // 当前行长度
            int length = renderInfoText.length();
            // 单个字符的长度
            float wordWidth = rect.width / length;
            // 指定字符串首次出现的索引
            int i = renderInfoText.indexOf(text);

            Float fitWidth = Objects.isNull(pdfStamperConfig.getFitWidth()) ? GlobalConfig.PDF_STAMPER_DEFAULT_FIT_WIDTH : pdfStamperConfig.getFitWidth();
            Float fitHeight = Objects.isNull(pdfStamperConfig.getFitHeight()) ? GlobalConfig.PDF_STAMPER_DEFAULT_FIT_HEIGHT : pdfStamperConfig.getFitHeight();
            float fitWidthRadius = 0f, fitHeightRadius = 0f;
            if (fitWidth > 0) {
                fitWidthRadius = fitWidth / 2;
            }
            if (fitHeight > 0) {
                fitHeightRadius = fitHeight / 2;
            }
            // 偏移量
            Float xOffset = Objects.isNull(pdfStamperConfig.getXOffset()) ? 0F : pdfStamperConfig.getXOffset();
            Float yOffset = Objects.isNull(pdfStamperConfig.getYOffset()) ? 0F : pdfStamperConfig.getYOffset();
            // 设置印章的XY坐标
            float x, y;
            if (rect.x < 60) {
                x = wordWidth * i + fitHeightRadius + xOffset;
            } else {
                x = rect.x + xOffset;
            }
            y = rect.y - fitWidthRadius + yOffset;
            location.setY(y > 0 ? y : 0);
            location.setX(x > 0 ? x : 0);
            log.info("text:{}, location:{}", text, location);
        }
    }

    @Override
    public void endTextBlock() {
    }

    @Override
    public void renderImage(ImageRenderInfo renderInfo) {
    }
}
其他配置类:
package com.lifengdi.model.pdf;

import lombok.Data;

/**
 * PDF模板配置
 * @author 李锋镝
 * @date Create at 11:10 2019/5/9
 */
@Data
public class PDFTempFile {
    /**
     * PDF模板文件类型
     */
    private String pdfType;

    /**
     * 模板文件名称
     */
    private String templateFileName;

    /**
     * 模板文件所在父级路径
     */
    private String templateFileParentPath;

    /**
     * 模板文件本地地址
     */
    private String templateFileLocalPath;
}
package com.lifengdi.config;

import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**
 * @author 李锋镝
 * @date Create at 10:51 2019/5/9
 */
@Getter
@Configuration
public class SystemConfig {
    /**
     * 文件本地缓存目录
     */
    @Value("${file.localTempPath}")
    private String localTempPath;

    /**
     * 项目中自带字体的目录
     */
    public static final String FONT_PATH_SONG = "font/simsun.ttf";

    public static final String SourceHanSansCN_Regular_TTF = "font/SourceHanSansCN-Regular.ttf";

    /**
     * 项目中的缓存目录
     */
    @Value("${file.appTempFolder:file/}")
    private String appTemplateFolder;
}
package com.lifengdi.model.pdf;

import lombok.Data;

/**
 * PDF印章配置
 * @author 李锋镝
 * @date Create at 11:16 2019/5/9
 */
@Data
public class PDFStamperConfig {

    /**
     * 是否生成印章
     */
    private Boolean generate = false;

    /**
     * 印章文件路径(PNG格式)
     */
    private String stamperUrl;

    /**
     * 生成印章的位置
     */
    private Location location;

    /**
     * 印章宽度
     */
    private Float fitWidth;

    /**
     * 印章高度
     */
    private Float fitHeight;

    /**
     * X偏移量
     */
    private Float xOffset;
    /**
     * Y偏移量
     */
    private Float yOffset;
}
package com.lifengdi.model.pdf;

import lombok.Data;

/**
 * PDF文件水印配置
 * @author 李锋镝
 * @date Create at 11:17 2019/5/9
 */
@Data
public class PDFWatermarkConfig {

    /**
     * 是否生成水印
     */
    private Boolean generate;

    /**
     * 水印文字
     */
    private String watermarkWord;

    /**
     * 水印文字颜色 red,green,blue
     */
    private String watermarkColor = "128,128,128";

    /**
     * 水印透明度
     */
    private Float fillOpacity = 0.1F;

    /**
     * 水印文字大小
     */
    private Integer fontSize = 38;

    /**
     * 水印文字字体
     */
    private String fontFamily;

    /**
     * 生成水印的位置,不指定则默认整页
     */
    private Location location;

}
package com.lifengdi.model.pdf;

import lombok.Data;

/**
 * 页面位置坐标
 * @author 李锋镝
 * @date Create at 11:35 2019/5/9
 */
@Data
public class Location {

    /**
     * X坐标
     */
    private Float x;

    /**
     * Y坐标
     */
    private Float y;

    /**
     * 横向间隔
     */
    private Float xInterval;

    /**
     * 纵向间隔
     */
    private Float yInterval;

    /**
     * 指定页 -1:全部,0:最后一页,1:首页
     */
    private Integer page;

    /**
     * 在指定文字上盖章
     */
    private String word;

    /**
     * 水印是否在内容上边
     */
    private Boolean overContent;
}
package com.lifengdi.model.pdf;

import com.lifengdi.model.IType;
import lombok.Data;

/**
 * @author 李锋镝
 * @date Create at 14:07 2019/5/9
 */
@Data
public class PDFType implements IType {

    private PDFTempFile pdfTempFile;

    private PDFStamperConfig pdfStamperConfig;

    private PDFWatermarkConfig pdfWatermarkConfig;
}
package com.lifengdi.config;

import com.lifengdi.model.pdf.PDFType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * PDF配置
 * @author 李锋镝
 * @date Create at 11:10 2019/5/9
 */
@Data
@Component
@ConfigurationProperties("pdf")
public class PDFConfig {

    private Map<String, PDFType> pdfType = new HashMap<>();
}

package com.lifengdi.config;

/**
 * @author 李锋镝
 * @date Create at 10:54 2019/5/9
 */
public class GlobalConfig {
    /**
     * 默认字符集
     */
    public static final String DEFAULT_ENCODING = "UTF-8";

    /**
     * PDF文件后缀
     */
    public static final String PDF_SUFFIX = ".pdf";

    /**
     * PDF印章默认宽度
     */
    public static final float PDF_STAMPER_DEFAULT_FIT_WIDTH = 100f;

    /**
     * PDF印章默认高度
     */
    public static final float PDF_STAMPER_DEFAULT_FIT_HEIGHT = 100f;

    /**
     * 默认字体大小
     */
    public static final float DEFAULT_FONT_SIZE = 38;
    /**
     * 默认水印X方向间隔
     */
    public static final float DEFAULT_X_INTERVAL = 38;
    /**
     * 默认水印Y方向间隔
     */
    public static final float DEFAULT_Y_INTERVAL = 38;
}
在application.yml配置如下:

# 生成PDF文件配置
pdf:
  pdfType:
    test:
      pdfTempFile:
        pdfType: test
        templateFileName: test.ftl
        templateFileParentPath: file/
      pdfStamperConfig:
        generate: true
# 印章所在路径
        stamperUrl: image/666.png
        xOffset: 0
        yOffset: 45
        fitWidth: 120
        fitHeight: 120
        location:
          x: -1
          y: -1
          page: 1
          word: 检测日期
      pdfWatermarkConfig:
        generate: true
        watermarkWord: 水印
        watermarkColor:
        fillOpacity:
        fontSize:
        fontFamily: SourceHanSansCN
        location:
          xInterval: 100
          yInterval: 120
          page:
          overContent: true
生成PDF效果截图:

源码地址:https://github.com/lifengdi/generate-file

 

除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.lifengdi.com/archives/article/861

相关文章

  • SpringBoot常用注解
  • CompletableFuture使用详解
  • SpringBoot 中内置的 49 个常用工具类
  • SpringBoot 实现接口防刷的 5 种实现方案
  • SpringBoot 实现 RSA+AES 自动接口解密
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: freemarker html itext JAVA PDF SpringBoot
最后更新:2022年3月14日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >

文章评论

  • 石头

    您好,MyStringUtil、GeneratedKey、 IType 这些类和接口没有呀

    2022年3月14日
    回复
    • 李锋镝

      @石头 文章最下面有源码链接,可以下载下源码看一下,里边都有的。

      2022年3月14日
      回复
      • 石头

        @李锋镝 好的好的,看见了,感谢

        2022年3月14日
        回复
  • 唐

    GlobalConfig类是什么?

    2019年11月11日
    回复
    • 李锋镝

      @唐 GlobalConfig是一个默认的配置类,文章已更新,翻到最下边就可以看到。

      2019年11月11日
      回复
  • 1 2 3 4 5 6 7 8 9 11 12 13 14 15 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 46 47 48 49 50 51 52 53 54 55 57 58 60 61 62 63 64 65 66 67 69 72 74 76 77 78 79 80 81 82 85 86 87 90 92 93 94 95 96 97 98 99
    回复 唐 取消回复

    曾伴浮云归晚翠,犹陪落日泛秋声。
    世间无限丹青手,一片伤心画不成。

    最新 热点 随机
    最新 热点 随机
    SpringBoot框架自动配置之spring.factories和AutoConfiguration.imports 应用型负载均衡(ALB)和网络型负载均衡(NLB)区别 什么是Helm? TransmittableThreadLocal介绍与使用 ReentrantLock深度解析 RedisTemplate和Redisson的区别
    玩博客的人是不是越来越少了?准备入手个亚太的ECS,友友们有什么建议吗?什么是Helm?2024年11月1号 农历十月初一别再背线程池的七大参数了,现在面试官都这么问URL地址末尾加不加“/”有什么区别
    内存耗尽后Redis会发生什么? linux中ftp查看不到文件列表的问题 iTerm2设置SSH自动连接服务器 IDEA下载源码报:Cannot connect to the Maven process. Try again later. 如何通过个人博客赚钱? 【PostgreSQL 15】PostgreSQL 15对UNIQUE和NULL的改进
    标签聚合
    K8s 设计模式 多线程 MySQL 教程 文学 Spring 日常 架构 数据库 分布式 IDEA JVM SpringBoot 面试 docker SQL Redis ElasticSearch JAVA
    友情链接
    • i架构
    • 临窗旋墨
    • 博友圈
    • 博客录
    • 博客星球
    • 哥斯拉
    • 志文工作室
    • 搬砖日记
    • 旋律的博客
    • 旧时繁华
    • 林羽凡
    • 知向前端
    • 蜗牛工作室
    • 集博栈
    • 韩小韩博客
    • 風の声音

    COPYRIGHT © 2025 lifengdi.com. ALL RIGHTS RESERVED.

    Theme Kratos Made By Dylan

    津ICP备2024022503号-3