html转word,使用模版引擎生成html,然后转word

<!--   模版引擎 -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>
        <!-- docx4j 核心库 -->
        <dependency>
            <groupId>org.docx4j</groupId>
            <artifactId>docx4j-JAXB-Internal</artifactId>
            <version>8.3.9</version>
        </dependency>
        <!-- HTML 转换支持 -->
        <dependency>
            <groupId>org.docx4j</groupId>
            <artifactId>docx4j-ImportXHTML</artifactId>
            <version>8.3.8</version>
        </dependency>
        <!-- 图片处理依赖 -->
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-codec</artifactId>
            <version>1.16</version>
        </dependency>
        <!--   模版引擎结束  -->

wordUtil

import cn.hutool.core.map.MapUtil;
import com.xxx.service.html2word.freemaker.HtmlGenerator;
import org.docx4j.Docx4J;
import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.relationships.Relationships;
import org.docx4j.wml.*;

import java.io.File;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Map;
import java.util.Optional;

/**
 * Word文档工具类
 * 用于生成带有页眉和内容的Word文档
 */
public class WordUtil {
    
    private static final ObjectFactory factory = new ObjectFactory();
    private static final HtmlGenerator htmlGenerator = new HtmlGenerator();

    // 字体样式常量
    private static final class FontStyle {
        static final String HEITI = "黑体";
        static final String FANGSONG = "仿宋";
        static final String SONGTI = "宋体";
        
        static final int HEADER_TITLE_SIZE = 36;  // 页眉标题字号
        static final int HEADER_TEXT_SIZE = 24;   // 页眉正文字号
        
        static final int TAB_POSITION = 9350;     // 制表符位置
    }

    /**
     * 生成Word文档
     * @param template HTML模板名称
     * @param data 数据Map,包含headerText1(页眉第一行)和headerText2(页眉第二行)
     * @param outputFilePath 输出流
     * @throws Exception 处理异常
     */
    public static void toWord(String template, Map<String,Object> data, OutputStream outputFilePath) throws Exception {
        WordprocessingMLPackage wordPackage = createWordPackage(template, data);
        saveWordDocument(wordPackage, outputFilePath);
    }

    /**
     * 创建Word文档包
     */
    private static WordprocessingMLPackage createWordPackage(String template, Map<String,Object> data) throws Exception {
        WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage();
        
        // 添加页眉
        addHeader(wordPackage, data);
        
        // 添加文档内容
        addDocumentContent(wordPackage, template, data);
        
        return wordPackage;
    }

    /**
     * 添加文档内容
     */
    private static void addDocumentContent(WordprocessingMLPackage wordPackage, String template, Map<String,Object> data) throws Exception {
        String html = htmlGenerator.generate(template, data);
        XHTMLImporterImpl importer = new XHTMLImporterImpl(wordPackage);
        importer.setHyperlinkStyle("Hyperlink");
        wordPackage.getMainDocumentPart().getContent().addAll(importer.convert(html, null));
    }

    /**
     * 保存Word文档
     */
    private static void saveWordDocument(WordprocessingMLPackage wordPackage, OutputStream outputFilePath) throws Exception {
        if (outputFilePath != null) {
            Docx4J.save(wordPackage, outputFilePath, Docx4J.FLAG_SAVE_ZIP_FILE);
        } else {
            Docx4J.save(wordPackage, new File("output22.docx"), Docx4J.FLAG_SAVE_ZIP_FILE);
        }
    }

    /**
     * 添加页眉
     */
    private static void addHeader(WordprocessingMLPackage wordPackage, Map<String,Object> data) throws Exception {
        HeaderPart headerPart = new HeaderPart();
        wordPackage.getMainDocumentPart().addTargetPart(headerPart);
        
        Hdr hdr = factory.createHdr();
        addHeaderTitle(hdr, MapUtil.getStr(data, "headerTitle"));
        addHeaderText(hdr, MapUtil.getStr(data, "headerText"));
        
        headerPart.setJaxbElement(hdr);
        setupHeaderReference(wordPackage, headerPart);
    }

    /**
     * 设置页眉引用
     */
    private static void setupHeaderReference(WordprocessingMLPackage wordPackage, HeaderPart headerPart) throws Docx4JException {
        Body body = wordPackage.getMainDocumentPart().getContents().getBody();
        SectPr sectPr = Optional.ofNullable(body.getSectPr())
                .orElseGet(() -> {
                    SectPr newSectPr = factory.createSectPr();
                    body.setSectPr(newSectPr);
                    return newSectPr;
                });

        MainDocumentPart mainDocumentPart = wordPackage.getMainDocumentPart();
        Relationships relationships = mainDocumentPart.getRelationshipsPart().getRelationships();
        
        relationships.getRelationship().stream()
                .filter(item -> "header.xml".equals(item.getTarget()))
                .findFirst()
                .ifPresent(relationship -> {
                    HeaderReference headerRef = factory.createHeaderReference();
                    headerRef.setType(HdrFtrRef.DEFAULT);
                    headerRef.setId(relationship.getId());
                    sectPr.getEGHdrFtrReferences().add(headerRef);
                });
    }

    /**
     * 添加页眉标题(第一行)
     */
    private static void addHeaderTitle(Hdr hdr, String headerText) {
        if (headerText == null) return;

        P p = factory.createP();
        setParagraphAlignment(p, JcEnumeration.CENTER);
        
        R r = factory.createR();
        Text text = createText(headerText);
        r.getContent().add(text);
        
        // 设置字体样式
        RPr rpr = factory.createRPr();
        setFont(rpr, FontStyle.HEITI, FontStyle.HEADER_TITLE_SIZE, true);
        r.setRPr(rpr);
        
        p.getContent().add(r);
        hdr.getContent().add(p);
    }

    /**
     * 添加页眉文本(第二行)
     */
    private static void addHeaderText(Hdr hdr, String headerText) {
        if (headerText == null) return;

        P p = factory.createP();
        PPr ppr = factory.createPPr();
        p.setPPr(ppr);

        // 添加左侧文本
        addLeftText(p, headerText);
        
        // 添加制表符
        addTab(p);
        
        // 添加右侧页码
        addPageNumber(p);
        
        // 设置制表符位置
        setTabStop(ppr);
        
        hdr.getContent().add(p);
    }

    /**
     * 添加左侧文本
     */
    private static void addLeftText(P p, String text) {
        R r = factory.createR();
        r.getContent().add(createText(text));
        
        RPr rpr = factory.createRPr();
        setFont(rpr, FontStyle.FANGSONG, FontStyle.HEADER_TEXT_SIZE, false);
        r.setRPr(rpr);
        
        p.getContent().add(r);
    }

    /**
     * 添加页码
     */
    private static void addPageNumber(P p) {
        R r = factory.createR();
        
        // 添加"第"字
        r.getContent().add(createText("第"));
        
        // 添加页码域
        addPageField(r);
        
        // 添加"页"字
        r.getContent().add(createText("页"));
        
        // 设置字体
        RPr rpr = factory.createRPr();
        setFont(rpr, FontStyle.SONGTI, FontStyle.HEADER_TEXT_SIZE, false);
        r.setRPr(rpr);
        
        p.getContent().add(r);
    }

    /**
     * 添加页码域
     */
    private static void addPageField(R r) {
        FldChar fldCharBegin = factory.createFldChar();
        fldCharBegin.setFldCharType(STFldCharType.BEGIN);
        r.getContent().add(fldCharBegin);
        
        Text pageNum = createText(" PAGE ");
        pageNum.setSpace("preserve");
        r.getContent().add(pageNum);
        
        FldChar fldCharEnd = factory.createFldChar();
        fldCharEnd.setFldCharType(STFldCharType.END);
        r.getContent().add(fldCharEnd);
    }

    /**
     * 创建文本对象
     */
    private static Text createText(String value) {
        Text text = factory.createText();
        text.setValue(value);
        return text;
    }

    /**
     * 添加制表符
     */
    private static void addTab(P p) {
        R r = factory.createR();
        R.Tab tab = factory.createRTab();
        r.getContent().add(tab);
        p.getContent().add(r);
    }

    /**
     * 设置制表符位置
     */
    private static void setTabStop(PPr ppr) {
        Tabs tabs = factory.createTabs();
        CTTabStop tabStop = factory.createCTTabStop();
        tabStop.setVal(STTabJc.RIGHT);
        tabStop.setPos(BigInteger.valueOf(FontStyle.TAB_POSITION));
        tabs.getTab().add(tabStop);
        ppr.setTabs(tabs);
    }

    /**
     * 设置段落对齐方式
     */
    private static void setParagraphAlignment(P p, JcEnumeration alignment) {
        PPr ppr = factory.createPPr();
        Jc jc = factory.createJc();
        jc.setVal(alignment);
        ppr.setJc(jc);
        p.setPPr(ppr);
    }

    /**
     * 设置字体样式
     */
    private static void setFont(RPr rpr, String fontName, int fontSize, boolean bold) {
        // 设置字体
        RFonts rFonts = factory.createRFonts();
        rFonts.setAscii(fontName);
        rFonts.setEastAsia(fontName);
        rpr.setRFonts(rFonts);
        
        // 设置字号
        HpsMeasure size = new HpsMeasure();
        size.setVal(BigInteger.valueOf(fontSize));
        rpr.setSz(size);
        
        // 设置加粗
        if (bold) {
            BooleanDefaultTrue bdt = new BooleanDefaultTrue();
            bdt.setVal(Boolean.TRUE);
            rpr.setB(bdt);
        }
    }
}
import java.net.URL;


public class ResourceLoader  {
	public static String CLASS_PATH_PREFIX ="classpath:";

	/**
	 * classpath中获取资源
	 * @Title: getResource
	 * @Description: classpath中获取资源
	 * @param resource
	 * @return
	 * @author lizp-c
	 */
	public static URL getResource(String resource) {
		ClassLoader classLoader = null;
		classLoader = Thread.currentThread().getContextClassLoader();
		return classLoader.getResource(resource);
	}

	/**
	 *  classpath 中搜索路径
	 * @Title: getPath
	 * @Description:
	 * @param resource
	 * @return
	 * @author lizp-c
	 */
	public static String getPath(String resource){
		if(resource!=null){
			if(resource.startsWith(CLASS_PATH_PREFIX)){
				resource = getPath("")+resource.replaceAll(CLASS_PATH_PREFIX, "");
			}
		}

		URL url = getResource(resource);
		if(url==null)
			return null;
		return url.getPath().replaceAll("%20", " ");
	}
	/**
	 *
	 * @Title: getPath
	 * @Description:
	 * @param resource
	 * @param clazz
	 * @return
	 * @author lizp-c
	 */
	public static String getPath(String resource,Class clazz){
		URL url = getResource(resource, clazz);
		if(url==null)
			return null;
		return url.getPath().replaceAll("%20", " ");
	}

	/**
	 * 指定class中获取资源
	 * @Title: getResource
	 * @Description: 指定class中获取资源
	 * @param resource
	 * @param clazz
	 * @return
	 * @author lizp-c
	 */
	public static URL getResource(String resource,Class clazz){
		return clazz.getResource(resource);
	}

	/**
	 * If running under JDK 1.2 load the specified class using the
	 * <code>Thread</code> <code>contextClassLoader</code> if that fails try
	 * Class.forname. Under JDK 1.1 only Class.forName is used.
	 *
	 */
	public static Class loadClass(String clazz) throws ClassNotFoundException {
		return Class.forName(clazz);
	}
import freemarker.template.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
/**
 * freemark配置
 * @ClassName: FreemarkerConfiguration
 * @Description: freemark配置
 * @author lizp-c
 */
public class FreemarkerConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(FreemarkerConfiguration.class);
    private static Configuration config = null;

    /**
     * 获取 FreemarkerConfiguration
     *
     * @Title: getConfiguation
     * @Description:
     * @return
     */
    public static synchronized Configuration getConfiguration() {
        if (config == null) {
            setConfiguation();
        }
        return config;
    }
    /**
     * 设置 配置
     * @Title: setConfiguation
     * @Description:
     */
    private static void setConfiguation() {
        config = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        String path = ResourceLoader.getPath("");
        try {
            config.setDirectoryForTemplateLoading(new File(path));
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }

}
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
    
public class HtmlGenerator {     
         
    /**   
     * Generate html string.   
     *    
     * @param template   the name of freemarker teamlate.   
     * @param variables  the data of teamlate.   
     * @return htmlStr   
     * @throws IOException 
     * @throws TemplateException 
     * @throws Exception   
     */    
    public String generate(String template, Map<String,Object> variables) throws IOException, TemplateException{     
        BufferedWriter writer = null;   
        String htmlContent;
        try{
        	Configuration config = FreemarkerConfiguration.getConfiguration();
        	config.setClassForTemplateLoading(this.getClass(),"/template");
        	config.setDefaultEncoding("UTF-8");
        	Template tp = config.getTemplate(template);
        	StringWriter stringWriter = new StringWriter();       
        	writer = new BufferedWriter(stringWriter);  
        	
        	tp.setEncoding("UTF-8");
        	tp.process(variables, writer);       
        	htmlContent = stringWriter.toString();     
        	writer.flush();       
        	
        }finally{
        	if(writer!=null)
        		writer.close();     
        }
        return htmlContent;     
    }     
} 

resources下放置template目录,然后放置html模版。

<html>
<head>
    <title></title>
</head>
<body>
<div>
    <table cellspacing="0" cellpadding="0">
        <tr style="height:28.35pt">
            <td>
                <p>
                    <span>工程名称</span>
                </p>
            </td>
            <td colspan="3">
                <p >
                    <span>${name}</span>
                </p>
            </td>
        </tr>
  </table>
上一篇 下一篇