用Spring MVC和Freemarker实现导出PDF

本文介绍了一个将HTML转换为PDF文件的实用案例,包括使用Freemarker生成HTML字符串、调整图片尺寸及利用Java进行PDF渲染的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网上已经有比较多的例子,但是很多都是简单的 demo,而且有很多隐藏的问题,本人再次写一个完整的demo:
Spring MVC部分:
            response.reset();
            response.setContentType("application/pdf;charset=utf-8");
            fileName =  encodeFilename(request, fileName);
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".pdf");
            OutputStream rtnos = response.getOutputStream();
            String htmlStr = HtmlGenerator.generate("sample.ftl", resultMap);
            String imgWidth = "width=\"450\"";
            String imgHeight = "height=\"300\"";
            htmlStr = htmlStr.replaceAll("width=\"(.*?)\"", imgWidth).replaceAll("height=\"(.*?)\"", imgHeight);
            logger.debug("===htmlStr===" + StringEscapeUtils.unescapeHtml(htmlStr));
            PdfGenerator.generate(StringEscapeUtils.unescapeHtml(htmlStr).replaceAll("&", "&"), rtnos);
            return null;
通过Freemarker模板生成html:
import java.io.BufferedWriter;  
import java.io.StringWriter;  
import java.util.Map;  

import freemarker.template.Configuration;  
import freemarker.template.Template;  
  
public class HtmlGenerator {  
      
    /** 
     * Generate html string. 
     *  
     * @param template   the name of freemarker teamlate. 
     * @param variables  the data of teamlate. 
     * @return htmlStr 
     * @throws Exception 
     */  
    public static String generate(String template, Map<String,Object> variables) throws Exception{  
        Configuration config = FreemarkerConfiguration.getConfiguation();  
        config.setDefaultEncoding("UTF-8");
        Template tp = config.getTemplate(template);  
        StringWriter stringWriter = new StringWriter();    
        BufferedWriter writer = new BufferedWriter(stringWriter);    
        tp.setEncoding("UTF-8");    
        tp.process(variables, writer);    
        String htmlStr = stringWriter.toString();  
        writer.flush();    
        writer.close();  
        return htmlStr;  
    }  
  
}  

html转PDF:

import java.io.ByteArrayInputStream;
import java.io.OutputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextRenderer;

public class PdfGenerator {

    /**
     * Output a pdf to the specified outputstream
     * 
     * @param htmlStr the htmlstr
     * @param out the specified outputstream
     * @throws Exception
     */
    public static void generate(String htmlStr, OutputStream out) throws Exception {
        DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = builder.parse(new ByteArrayInputStream(htmlStr.getBytes()));
        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocument(doc, null);
        renderer.layout();
        renderer.createPDF(out);
        out.flush();
        out.close();
    }

}

sample.ftl(片段):

<#list data as note>
			    <li data-id="12" data-nodetime="${(note.nodeTime)!}" data-totaltime="${(note.totalTime)!}" data-nodename="${(note.nodeName)!}" data-imgs="${(note.imgsStr)!}" data-content="${(note.content)!}">
			        <div class="p-notelist-cite-wrap">
			            <<#if note.reference == "video">a<#else>span</#if> class="p-notelist-cite" href="${(note.subjectUrl)!}">
			               	 引用<i <#if note.reference == "video">class="i-icon i-icon-paly "<#else>class="i-icon i-icon-note "</#if>></i>${(note.nodeName)!}
			                <span class="p-time"> ${(note.nodeTime)!}/${(note.totalTime)!} </span> <#if note.reference == "video"><b>提纲</b></#if>
			            </<#if note.reference == "video">a<#else>span</#if>>
			            <div class="p-cite-content">
		                    <div class="p-cite-content-wrap">
			                    <div class="i-subject-content">
				                    <div class="subject subject-choice">
									    <div class="stem">
									        <span class="type">[单选]</span>
									        <span class="use-number"></span>
									        <p>A是老王</p><img src="http://kaoshi.neibu.koolearn.com/upload/image/1430791311563.png"></img>
									        <p>B是老张</p>
									        <p>谁是凶手</p>
									    </div>
									    <div class="answer">
									        <ul>
									            <li>
									                <span data-id="0" class="number">A</span>
									                <span class="content">A是凶手</span>
									            </li>
									            <li>
									                <span data-id="1" class="number">B</span>
									                <span class="content">B是凶手</span>
									            </li>
									        </ul>
									    </div>
									</div>
								</div>
							</div>
		                    <a href="/project/teach/1.x/demo/apply/searchResult.html" target="_blank" class="p-view"><i class="i-icon i-icon-edit-hover"></i>在做题记录中查看</a>
		                </div>
			        </div>
			        <div class="p-note-content">
			            <span>${(note.content)!}</span>
			            <#if note.imgs??>
                			<#list note.imgs as img>
			                    <img src="${(img)!}" alt=""></img>
			            	</#list>
			            </#if>
			        </div>
			        <div class="p-note-bottom">
			            <i class="i-icon i-icon-notemark"></i><span class="p-note-mark">${(note.mark)!}</span>
			            <div class="p-note-operate">
			                <span>${(note.time)!}</span>
			            </div>
			        </div>
			    </li>
			</#list>


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值