liming1320 2022-03-03 18:46 采纳率: 0%
浏览 183
已结题

使用itextpdf把HTML转为pdf文件的问题,占位符${}的长度问题,导致的换行,使模板和签约的合同位置坐标不一样

使用itextpdf把HTML转为pdf文件的问题,占位符${}的长度问题,导致的换行,使模板和签约的合同位置坐标不一样
从下图往后面就越差越多,导致盖章的位置也不一样。(html那些是加的表格之类的样式)
请问这种问题应该怎么解决。使模板和填写的内容的合同完全一样

img

@Component
public class PDFTemplateUtil {

private static Logger logger = LoggerFactory.getLogger(PDFTemplateUtil.class);

@Autowired
private SysOssService sysOssService;
@Value("${config.pdfUrl}")
private String pdfUrl;

/**
 * classpath路径
 */
private String classpath = getClass().getResource("/").getPath();

/**
 * 指定FreeMarker模板文件的位置
 */
private String templatePath = "/pdf";

/**
 * 字体资源文件 存放路径 图片路径
 */
private String fontPath = "template/";

/**
 * 字体   [宋体][simsun.ttc]   [黑体][simhei.ttf]
 */
private String font = "simsun.ttc";

/**
 * 指定编码
 */
private String encoding = "UTF-8";

/**
 * 获取Freemarker渲染后的内容
 *
 * @param content 模板内容
 * @param params  参数
 */
private String getFreemarkerContent(String content, Map<String, Object> params) throws Exception {
    if (MapUtil.isEmpty(params)) {
        return content;
    }

    //模板
    StringReader reader = new StringReader(content);
    Template template = new Template("mail", reader, null, "utf-8");

    //渲染模板
    StringWriter sw = new StringWriter();
    template.process(params, sw);

    content = sw.toString();
    IOUtils.closeQuietly(sw);

    return content;
}

/**
 * 生成pdf
 *
 * @param content 模板内容
 * @param params  传入到freemarker模板里的数据
 */
public String createPDF(String content, String params) throws FileNotFoundException {
    String url = "";
    UUID uuid = UUID.randomUUID();
    LocalDate date = LocalDate.now();
    String filePath = pdfUrl + fontPath + date + uuid + ".pdf";
    FileOutputStream out = new FileOutputStream(new File(filePath));
    // 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
    Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
    // 指定FreeMarker模板文件的位置
    cfg.setClassForTemplateLoading(getClass(), templatePath);
    ITextRenderer renderer = new ITextRenderer();
    try {
        String freemarkerContent = "";
        if (params != null) {
            Map map = JSONObject.parseObject(params, Map.class);
            freemarkerContent = getFreemarkerContent(content, map);
        } else {
            freemarkerContent = content;
        }

        //设置utf-8编码
        //EmailConfig config = sysParamsService.getValueObject(Constant.MAIL_CONFIG_KEY, EmailConfig.class);
        //JavaMailSenderImpl mailSender = createMailSender(config);
        //MimeMessage mimeMessage = mailSender.createMimeMessage();
        //MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
        //messageHelper.setText(content, true);
        //freemarkerContent = freemarkerContent.replace("<br>", "<br></br>");
        //div格式转换过程中,有几率会使内容消失
        freemarkerContent = freemarkerContent.replaceAll("<div", "<span");
        freemarkerContent = freemarkerContent.replaceAll("/div>", "/span><br/>");
        //关闭未封闭式标签br
        freemarkerContent = freemarkerContent.replaceAll("<br[^>]*>", "<br/>");
        //处理样式background-color: transparent; IE模式下存在兼容问题,火狐谷歌正常
        freemarkerContent = freemarkerContent.replaceAll("background-color: transparent;", " ");
        //freemarkerContent = freemarkerContent.replaceAll(" ", "\u00a0");

        freemarkerContent = freemarkerContent.replaceAll("\t","\u00a0\u00a0\u00a0\u00a0");
        // 不替换标签中的空格
        freemarkerContent = textString(freemarkerContent);

        freemarkerContent = freemarkerContent.replaceAll("><col width=\"", "/><col width=\"");
        freemarkerContent = freemarkerContent.replaceAll("></colgroup>", "/></colgroup>");
        freemarkerContent = freemarkerContent.replaceAll("<col width=\"100\">", "<col width=\"100\"/>");
        freemarkerContent = freemarkerContent.replaceAll("<colgroup/>", "<colgroup>");
        StringBuffer html = new StringBuffer();
        html.append("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN/' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>");
        html.append("<html xmlns='http://www.w3.org/1999/xhtml'>");
        html.append("<head><style>body{font-family:SimSun;font-size:14px;}</style> ");//此处一定要加,不然字体也不会显示,不要加line-height属性
        html.append("<title>" + "无标题文档" + "</title>");
        html.append("<style type=\"text/css\">");
        html.append(".qlbt-operation-menu {\n" +
                "  background-color: #fff;\n" +
                "  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n" +
                "  font-size: 14px;\n" +
                "  z-index: 100;\n" +
                "  overflow: hidden; }\n" +
                "  .qlbt-operation-menu .qlbt-operation-menu-dividing {\n" +
                "    height: 1px;\n" +
                "    background-color: #efefef; }\n" +
                "  .qlbt-operation-menu .qlbt-operation-menu-subtitle {\n" +
                "    color: #999;\n" +
                "    font-size: 14px;\n" +
                "    padding: 5px 16px; }\n" +
                "  .qlbt-operation-menu .qlbt-operation-color-picker {\n" +
                "    display: flex;\n" +
                "    align-items: center;\n" +
                "    flex-wrap: wrap;\n" +
                "    padding: 0 16px 10px;\n" +
                "    background-color: #fff;\n" +
                "    overflow: hidden; }\n" +
                "    .qlbt-operation-menu .qlbt-operation-color-picker .qlbt-operation-color-picker-item {\n" +
                "      width: 20px;\n" +
                "      height: 20px;\n" +
                "      border: 1px solid #595959;\n" +
                "      margin-right: 5px;\n" +
                "      margin-bottom: 5px;\n" +
                "      cursor: pointer; }\n" +
                "  .qlbt-operation-menu .qlbt-operation-menu-item {\n" +
                "    display: flex;\n" +
                "    align-items: center;\n" +
                "    padding: 10px 16px;\n" +
                "    background-color: #fff;\n" +
                "    cursor: pointer;\n" +
                "    color: #595959;\n" +
                "    overflow: hidden;\n" +
                "    text-overflow: ellipsis; }\n" +
                "    .qlbt-operation-menu .qlbt-operation-menu-item:hover {\n" +
                "      background-color: #efefef; }\n" +
                "    .qlbt-operation-menu .qlbt-operation-menu-item .qlbt-operation-menu-icon {\n" +
                "      margin-right: 8px;\n" +
                "      height: 20px;\n" +
                "      width: 20px;\n" +
                "      font-size: 0; }");
        html.append(".qlbt-col-tool {\n" +
                "  position: absolute;\n" +
                "  display: flex;\n" +
                "  align-items: flex-end;\n" +
                "  overflow: hidden;\n" +
                "  z-index: 99;\n" +
                "  height: 16px; }\n" +
                "  .qlbt-col-tool .qlbt-col-tool-cell {\n" +
                "    position: relative;\n" +
                "    background-color: #fff;\n" +
                "    border-top: 1px solid #000;\n" +
                "    border-right: 1px solid #000;\n" +
                "    border-bottom: 1px solid #000; }\n" +
                "    .qlbt-col-tool .qlbt-col-tool-cell:first-child {\n" +
                "      border-left: 1px solid #000; }\n" +
                "  .qlbt-col-tool .qlbt-col-tool-cell-holder {\n" +
                "    position: absolute;\n" +
                "    right: -1px;\n" +
                "    top: 0;\n" +
                "    bottom: 0;\n" +
                "    z-index: 3;\n" +
                "    width: 1px;\n" +
                "    background-color: transparent;\n" +
                "    cursor: ew-resize; }\n" +
                "    .qlbt-col-tool .qlbt-col-tool-cell-holder:hover {\n" +
                "      background-color: #0589f3; }\n" +
                "    .qlbt-col-tool .qlbt-col-tool-cell-holder::before {\n" +
                "      content: \"\";\n" +
                "      position: absolute;\n" +
                "      top: 0;\n" +
                "      left: -6px;\n" +
                "      display: block;\n" +
                "      width: 8px;\n" +
                "      height: 100%; }\n" +
                "    .qlbt-col-tool .qlbt-col-tool-cell-holder::after {\n" +
                "      content: \"\";\n" +
                "      position: absolute;\n" +
                "      top: 0;\n" +
                "      right: -6px;\n" +
                "      display: block;\n" +
                "      width: 8px;\n" +
                "      height: 100%; }\n");
        html.append(".quill-better-table-wrapper {\n" +
                "  overflow-x: auto; }");
        html.append("table.quill-better-table {\n" +
                "  table-layout: fixed;\n" +
                "  border-collapse: collapse; }\n" +
                "  table.quill-better-table td {\n" +
                "    border: 1px solid #000;\n" +
                "    padding: 2px 5px; }");
        /*html.append("table{\n" +
                " page-break-inside:auto;-fs-table-paginate:paginate;border-spacing:0;table-layout:fixed;       " +
                "word-break:break-strict; cellspacing:0;cellpadding:0; padding: 2px 2px;}");*/
        html.append("tr { page-break-inside:avoid; page-break-after:auto;}");
        html.append(".ql-align-center {\n" +
                "text-align: center;\n" +
                "}");
        html.append(".no-border td {\n" +
                "  border: 0;\n" +
                "}");
        html.append("</style>");
        html.append("</head>");
        html.append("<body>");
        html.append("<div>" + freemarkerContent + "</div>");//这里根据实际情况替换成你的HTML代码就OK了
        html.append("</body></html>");
        // 把html代码传入渲染器中
        renderer.setDocumentFromString(html.toString());

        // 解决图片的相对路径问题 ##必须在设置document后再设置图片路径,不然不起作用
        // 如果使用绝对路径依然有问题,可以在路径前面加"file:/"
        // 设置 css中 的字体样式(暂时仅支持宋体和黑体)
        ITextFontResolver fontResolver = renderer.getFontResolver();
        fontResolver.addFont(pdfUrl + fontPath + font, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        // 解决中文支持问题
        renderer.getSharedContext().setBaseURL(pdfUrl + fontPath);
        renderer.layout();
        renderer.createPDF(out, false);
        renderer.finishPDF();
        out.flush();
        out.close();

        File file = new File(filePath);
        FileInputStream fileInputStream = new FileInputStream(file);
        MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(),
                ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
        //上传文件
        String extension = FilenameUtils.getExtension(multipartFile.getOriginalFilename());
        url = OSSFactory.build().uploadSuffix(multipartFile.getBytes(), extension);

        //保存文件信息
        SysOssEntity ossEntity = new SysOssEntity();
        ossEntity.setUrl(url);
        ossEntity.setCreateDate(new Date());
        sysOssService.insert(ossEntity);

        file.delete();
    } catch (Exception e) {
        logger.error("表格生成失败 - {}" + e);
        e.printStackTrace();
    }
    return url;
}

public static String textString(String str) {
    String result = "";
    char[] c = str.toCharArray();
    int length = c.length;
    int i = 0;
    while (i < length) {
        String temp = "";
        while (i < length && c[i] != '<') {
            temp += c[i++];
        }
        result += temp.replaceAll(" ", "\u00a0");
        while (i < length && c[i] != '>') {
            result += c[i++];
        }
    }
    return result;
}

private JavaMailSenderImpl createMailSender(EmailConfig config) {
    JavaMailSenderImpl sender = new JavaMailSenderImpl();
    sender.setHost(config.getSmtp());
    sender.setPort(config.getPort());
    sender.setUsername(config.getUsername());
    sender.setPassword(config.getPassword());
    sender.setDefaultEncoding("Utf-8");
    Properties p = new Properties();
    p.setProperty("mail.smtp.timeout", "10000");
    p.setProperty("mail.smtp.auth", "false");
    p.setProperty("mail.smtp.ssl.enable", "true");
    sender.setJavaMailProperties(p);
    return sender;
}

public void setClasspath(String classpath) {
    this.classpath = classpath;
}

public void setTemplatePath(String templatePath) {
    this.templatePath = templatePath;
}

public void setFontPath(String fontPath) {
    this.fontPath = fontPath;
}

public void setFont(String font) {
    this.font = font;
}

public void setEncoding(String encoding) {
    this.encoding = encoding;
}

@Override
public String toString() {
    return "[templatePath] : " + templatePath + " \r\n "
            + "[fontPath] : " + fontPath + " \r\n "
            + "[font] : " + font + " \r\n "
            + "[encoding] : " + encoding;
}

}

  • 写回答

0条回答 默认 最新

    报告相同问题?

    问题事件

    • 系统已结题 3月11日
    • 创建了问题 3月3日

    悬赏问题

    • ¥15 关于大棚监测的pcb板设计
    • ¥15 stm32开发clion时遇到的编译问题
    • ¥15 lna设计 源简并电感型共源放大器
    • ¥15 如何用Labview在myRIO上做LCD显示?(语言-开发语言)
    • ¥15 Vue3地图和异步函数使用
    • ¥15 C++ yoloV5改写遇到的问题
    • ¥20 win11修改中文用户名路径
    • ¥15 win2012磁盘空间不足,c盘正常,d盘无法写入
    • ¥15 用土力学知识进行土坡稳定性分析与挡土墙设计
    • ¥15 帮我写一个c++工程