参考:https://github.com/superad/pdf-kit.git

预览:

hello.ftl

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><meta http-equiv="Content-Style-Type" content="text/css"/><title></title><style type="text/css">body {font-family: pingfang sc light;}.center {text-align: center;width: 100%;}</style>
</head>
<body>
<!--第一页开始-->
<div class="page"><div class="center"><p>${templateName}</p></div>
<#--    <div><p>iText官网:${ITEXTUrl}</p></div>--><div><p>FreeMarker官网:${freeMarkerUrl}</p></div>
<#--    <div><p>JFreeChart教程:${JFreeChartUrl}</p></div>-->
<#--    <!--外部链接&ndash;&gt;-->
<#--    <p>静态logo图</p>-->
<#--    <div>-->
<#--        <img src="${imageUrl}" alt="美团点评" width="110" height="20.8"/>-->
<#--    </div>-->
<#--    <!--动态生成的图片&ndash;&gt;-->
<#--    <p>气温变化对比图</p>-->
<#--    <div>-->
<#--        <img src="${picUrl}" alt="我的图片" width="500" height="270"/>-->
<#--    </div>-->
<#--    <!--jfreechart 散点图&ndash;&gt;-->
<#--    <p>发展建议分布图</p>-->
<#--    <div>-->
<#--        <img src="${scatterUrl}" alt="散点图" width="700" height="600"/>-->
<#--    </div>-->
</div>
<!--第一页结束-->
<!---分页标记-->
<!--<span style="page-break-after:always;"></span>-->
<!--第二页开始-->
<div class="page"><div>hello....开始了</div><div>列表值:</div><div><table border="1"><tr><th>Month</th></tr><#list scores as item><tr><td>${item}</td></tr></#list></table></div>
</div><!--第二页结束-->
</body>
</html>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.github.superad.pdf.kit</groupId><artifactId>pdf-kit</artifactId><packaging>jar</packaging><name>${project.artifactId}</name><version>1.1-RELEASE</version><properties><java.src.version>1.7</java.src.version><java.target.version>1.7</java.target.version><project.encoding>UTF-8</project.encoding></properties><parent><groupId>org.sonatype.oss</groupId><artifactId>oss-parent</artifactId><version>7</version></parent><dependencies><!--pdf生成工具类--><!--pdf生成 itext--><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.4.2</version></dependency><dependency><groupId>com.itextpdf.tool</groupId><artifactId>xmlworker</artifactId><version>5.4.1</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version></dependency><dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf</artifactId><version>9.0.3</version></dependency><!--freemarker--><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.26-incubating</version></dependency><!--jfreechart--><dependency><groupId>jfree</groupId><artifactId>jfreechart</artifactId><version>1.0.2</version></dependency><!--log--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>1.0.13</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.0.13</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-access</artifactId><version>1.0.13</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>log4j-over-slf4j</artifactId><version>1.7.21</version></dependency><!--util--><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>20.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.14.8</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-io</artifactId><version>1.3.2</version></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency><!--servlet--><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version><scope>provided</scope></dependency></dependencies><!--增加的配置,过滤ttf文件的匹配--><profiles><profile><id>release</id><build><resources><resource><directory>src/main/resources</directory><filtering>true</filtering></resource></resources><plugins><!--compile--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.5.1</version><configuration><source>${java.src.version}</source><target>${java.target.version}</target><encoding>${project.encoding}</encoding></configuration></plugin><!-- Source --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><version>2.2.1</version><executions><execution><phase>package</phase><goals><goal>jar-no-fork</goal></goals></execution></executions></plugin><!-- Javadoc --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-javadoc-plugin</artifactId><version>2.9.1</version><executions><execution><phase>package</phase><goals><goal>jar</goal></goals></execution></executions></plugin><!-- GPG --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-gpg-plugin</artifactId><version>1.5</version><executions><execution><phase>verify</phase><goals><goal>sign</goal></goals></execution></executions></plugin></plugins></build><distributionManagement><snapshotRepository><id>oss</id><url>https://oss.sonatype.org/content/repositories/snapshots/</url></snapshotRepository><repository><id>oss</id><url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url></repository></distributionManagement></profile></profiles><licenses><license><name>The Apache Software License, Version 2.0</name><url>http://www.apache.org/licenses/LICENSE-2.0.txt</url><distribution>repo</distribution></license></licenses>
</project>

package pdf.kit.component.builder;import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;public interface HeaderFooterBuilder {/*** @description 写页眉*/void writeHeader(PdfWriter writer, Document document, Object data, Font font, PdfTemplate template);/*** @description 写页脚*/void writeFooter(PdfWriter writer, Document document, Object data, Font font, PdfTemplate template);/*** @description 关闭文档前,获取替换页眉页脚处设置模板的文本*/String getReplaceOfTemplate(PdfWriter writer, Document document, Object data);
}
package pdf.kit.component.builder;import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pdf.kit.exception.PDFException;import java.io.IOException;/*** 设置页面附加属性**/
public class PDFBuilder extends PdfPageEventHelper {private static Logger log= LoggerFactory.getLogger(PDFBuilder.class);//字体文件名private String fontFileName;// 基础字体对象private  BaseFont bf;// 利用基础字体生成的字体对象,一般用于生成中文文字private  Font fontDetail;//文档字体大小private  int fontSize=12;//模板private  PdfTemplate template;//数据实体private  Object data;//页眉页脚定制接口private HeaderFooterBuilder headerFooterBuilder;//不允许空的构造方法private PDFBuilder() {}public PDFBuilder(HeaderFooterBuilder headerFooterBuilder) {this(headerFooterBuilder,null);}public PDFBuilder(HeaderFooterBuilder headerFooterBuilder, Object data) {this(headerFooterBuilder, data, "ping_fang_light.ttf");}public PDFBuilder(HeaderFooterBuilder headerFooterBuilder, Object data, String fontFileName) {this(headerFooterBuilder, data, fontFileName, 12);}public PDFBuilder(HeaderFooterBuilder headerFooterBuilder, Object data, String fontFileName, int fontSize) {this.headerFooterBuilder = headerFooterBuilder;this.data=data;this.fontFileName=fontFileName;this.fontSize=fontSize;}public void onOpenDocument(PdfWriter writer, Document document) {template = writer.getDirectContent().createTemplate(50, 50);}/**** 关闭每页的时候,写入页眉,页脚等**/public void onEndPage(PdfWriter writer, Document document) {this.addPage(writer, document);}//加分页private void addPage(PdfWriter writer, Document document){if(headerFooterBuilder !=null){//1.初始化字体initFront();//2.写入页眉headerFooterBuilder.writeHeader(writer,document,data,fontDetail,template);//3.写入前半部分页脚headerFooterBuilder.writeFooter(writer,document,data,fontDetail,template);}}/**** 关闭文档时,替换模板,完成整个页眉页脚组件**/public void onCloseDocument(PdfWriter writer, Document document) {if(headerFooterBuilder !=null){template.beginText();template.setFontAndSize(bf,fontSize);String replace= headerFooterBuilder.getReplaceOfTemplate(writer,document,data);template.showText(replace);template.endText();template.closePath();}}/*** @description 初始化字体*/private void initFront(){if(StringUtils.isEmpty(fontFileName)){throw new PDFException("PDF文档字体未设置!");}try {if (bf == null) {//添加字体,以支持中文String classpath=PDFBuilder.class.getClassLoader().getResource("").getPath();String fontPath =classpath+"fonts/"+fontFileName;//创建基础字体bf = BaseFont.createFont(fontPath,BaseFont.IDENTITY_H,BaseFont.EMBEDDED);}if (fontDetail == null) {fontDetail = new Font(bf, fontSize, Font.NORMAL);// 数据体字体log.info("PDF文档字体初始化完成!");}} catch (DocumentException e) {log.error("字体初始化失败{}", ExceptionUtils.getFullStackTrace(e));throw new PDFException("字体初始化失败",e);} catch (IOException e) {log.error("字体初始化失败{}", ExceptionUtils.getFullStackTrace(e));throw new PDFException("字体初始化失败",e);}}public int getPresentFontSize() {return fontSize;}public void setPresentFontSize(int fontSize) {this.fontSize = fontSize;}public String getFontFileName() {return fontFileName;}public void setFontFileName(String fontFileName) {this.fontFileName = fontFileName;}
}
package pdf.kit.component;import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;
import pdf.kit.component.builder.HeaderFooterBuilder;/*** 页眉页脚定制工具*/
public class PDFHeaderFooter implements HeaderFooterBuilder {/*** @param writer   PDF编写类* @param document PDF文档对象* @param data     业务数据* @param font     字体设置* @param template PDF模板* @description PDF页脚设置类*/public void writeFooter(PdfWriter writer, Document document, Object data, Font font, PdfTemplate template) {if (data == null) {return;}int pageS = writer.getPageNumber();int currentPage = pageS;if (currentPage <= 0) {return;}Phrase footer1 = new Phrase("页脚一", font);Phrase footer2 = new Phrase("页脚二" + "    " + currentPage + "/", font);PdfContentByte cb = writer.getDirectContent();ColumnText.showTextAligned(cb, Element.ALIGN_LEFT, footer1, (document.left() + 10), document.bottom() - 20, 0);ColumnText.showTextAligned(cb, Element.ALIGN_RIGHT, footer2, (document.right() - 30), document.bottom() - 20, 0);//设置模板位置cb.addTemplate(template, document.right() - 30, document.bottom() - 20);}/*** @param writer   PDF编写类* @param document PDF文档对象* @param data     业务数据* @param font     字体设置* @param template PDF模板* @description PDF页头设置类*/public void writeHeader(PdfWriter writer, Document document, Object data, Font font, PdfTemplate template) {ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, new Phrase("我是页眉", font), document.left(), document.top() + 20, 0);}/*** @param writer   PDF编写类* @param document PDF文档对象* @param data     业务数据* @description 页头、页眉设置的模板替换类*/public String getReplaceOfTemplate(PdfWriter writer, Document document, Object data) {int total = writer.getPageNumber() - 1;return total + "";}
}
package pdf.kit.component;import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.xhtmlrenderer.pdf.ITextRenderer;
import pdf.kit.component.builder.HeaderFooterBuilder;
import pdf.kit.component.builder.PDFBuilder;
import pdf.kit.exception.PDFException;
import pdf.kit.util.FreeMarkerUtil;import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.Charset;@Slf4j
public class PDFKit {//PDF页眉、页脚定制工具private HeaderFooterBuilder headerFooterBuilder;private String saveFilePath;/*** @description     导出pdf到文件* @param fileName  输出PDF文件名* @param data      模板所需要的数据**/public String exportToFile(String fileName,Object data){String htmlData= FreeMarkerUtil.getContent(fileName, data);if(StringUtils.isEmpty(saveFilePath)){saveFilePath=getDefaultSavePath(fileName);}File file=new File(saveFilePath);if(!file.getParentFile().exists()){file.getParentFile().mkdirs();}FileOutputStream outputStream=null;try{//设置输出路径outputStream=new FileOutputStream(saveFilePath);//设置文档大小Document document = new Document(PageSize.A4);PdfWriter writer = PdfWriter.getInstance(document, outputStream);//设置页眉页脚PDFBuilder builder = new PDFBuilder(headerFooterBuilder,data);builder.setPresentFontSize(10);writer.setPageEvent(builder);//输出为PDF文件convertToPDF(writer,document,htmlData);}catch(Exception ex){throw new PDFException("PDF export to File fail",ex);}finally{IOUtils.closeQuietly(outputStream);}return saveFilePath;}/*** 生成PDF到输出流中(ServletOutputStream用于下载PDF)* @param ftlPath ftl模板文件的路径(不含文件名)* @param data 输入到FTL中的数据* @param response HttpServletResponse* @return*/public OutputStream exportToResponse(String ftlPath,Object data, HttpServletResponse response){String html= FreeMarkerUtil.getContent(ftlPath,data);try{OutputStream out = null;ITextRenderer render = null;out = response.getOutputStream();//设置文档大小Document document = new Document(PageSize.A4);PdfWriter writer = PdfWriter.getInstance(document, out);//设置页眉页脚PDFBuilder builder = new PDFBuilder(headerFooterBuilder,data);writer.setPageEvent(builder);//输出为PDF文件convertToPDF(writer,document,html);return out;}catch (Exception ex){throw  new PDFException("PDF export to response fail",ex);}}/*** @description PDF文件生成*/private  void convertToPDF(PdfWriter writer,Document document,String htmlString){//获取字体路径String fontPath=getFontPath();document.open();try {XMLWorkerHelper.getInstance().parseXHtml(writer,document,new ByteArrayInputStream(htmlString.getBytes()),XMLWorkerHelper.class.getResourceAsStream("/default.css"),Charset.forName("UTF-8"),new XMLWorkerFontProvider(fontPath));} catch (IOException e) {e.printStackTrace();throw new PDFException("PDF文件生成异常",e);}finally {document.close();}}/*** @description 创建默认保存路径*/private  String  getDefaultSavePath(String fileName){String classpath = PDFKit.class.getClassLoader().getResource("").getPath();String saveFilePath = classpath + "pdf/" + fileName;File f = new File(saveFilePath);if(!f.getParentFile().exists()){f.mkdirs();}return saveFilePath;}/*** @description 获取字体设置路径*/public static String getFontPath() {String classpath=PDFKit.class.getClassLoader().getResource("").getPath();String fontpath=classpath+"fonts";return fontpath;}public HeaderFooterBuilder getHeaderFooterBuilder() {return headerFooterBuilder;}public void setHeaderFooterBuilder(HeaderFooterBuilder headerFooterBuilder) {this.headerFooterBuilder = headerFooterBuilder;}public String getSaveFilePath() {return saveFilePath;}public void setSaveFilePath(String saveFilePath) {this.saveFilePath = saveFilePath;}
}
package pdf.kit.util;import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Map;@Slf4j
public class FontUtil {private static  final Map<String,Font> fontCache= Maps.newConcurrentMap();public static String getFontPath(String fontName){String classPath=FontUtil.class.getClassLoader().getResource("").getPath();return classPath+"/fonts/"+fontName;}/*** @description 加载自定义字体*/public static Font loadFont(String fontFileName, int style, float fontSize) throws IOException {FileInputStream fis=null;String key=fontFileName+"|"+style;Font dynamicFont=fontCache.get(key);if(dynamicFont==null){try{File file = new File(fontFileName);fis= new FileInputStream(file);dynamicFont = Font.createFont(style, fis);fontCache.put(key,dynamicFont);}catch(Exception e){return new Font("宋体", Font.PLAIN, 14);}finally{fis.close();}}Font dynamicFontPt = dynamicFont.deriveFont(fontSize);return dynamicFontPt;}public static Font getFont(int style,float fontSize){try {String fontPath = FontUtil.getFontPath("SIMLI.TTF");Font font = loadFont(fontPath,style,fontSize);return font;} catch (IOException e) {log.error("字体加载异常{}", ExceptionUtils.getFullStackTrace(e));}return null;}
}
package pdf.kit.util;import com.google.common.collect.Maps;
import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import org.apache.commons.lang.StringUtils;
import pdf.kit.component.PDFKit;
import pdf.kit.exception.FreeMarkerException;
import pdf.kit.exception.PDFException;import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;/*** FREEMARKER 模板工具类*/
public class FreeMarkerUtil {private static final String WINDOWS_SPLIT = "\\";private static final String UTF_8="UTF-8";private static Map<String,FileTemplateLoader> fileTemplateLoaderCache=Maps.newConcurrentMap();private static  Map<String,Configuration> configurationCache= Maps.newConcurrentMap();public static Configuration getConfiguration(String templateFilePath){if(null!=configurationCache.get(templateFilePath)){return configurationCache.get(templateFilePath);}Configuration config = new Configuration(Configuration.VERSION_2_3_25);config.setDefaultEncoding(UTF_8);config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);config.setLogTemplateExceptions(false);FileTemplateLoader fileTemplateLoader=null;if(null!=fileTemplateLoaderCache.get(templateFilePath)){fileTemplateLoader=fileTemplateLoaderCache.get(templateFilePath);}try {fileTemplateLoader=new FileTemplateLoader(new File(templateFilePath));fileTemplateLoaderCache.put(templateFilePath,fileTemplateLoader);} catch (IOException e) {throw new FreeMarkerException("fileTemplateLoader init error!",e);}config.setTemplateLoader(fileTemplateLoader);configurationCache.put(templateFilePath,config);return config;}/*** @description 获取模板*/public static String getContent(String fileName, Object data) {String templatePath = getPDFTemplatePath(fileName);String templateFileName = getTemplateName(templatePath);String templateFilePath = getTemplatePath(templatePath);if (StringUtils.isEmpty(templatePath)) {throw new FreeMarkerException("templatePath can not be empty!");}try {Template template=getConfiguration(templateFilePath).getTemplate(templateFileName);StringWriter writer = new StringWriter();template.process(data, writer);writer.flush();return  writer.toString();} catch (Exception ex) {throw new FreeMarkerException("FreeMarkerUtil process fail", ex);}}private static String getTemplatePath(String templatePath) {if (StringUtils.isEmpty(templatePath)) {return "";}if (templatePath.contains(WINDOWS_SPLIT)) {return templatePath.substring(0, templatePath.lastIndexOf(WINDOWS_SPLIT));}return templatePath.substring(0, templatePath.lastIndexOf("/"));}private static String getTemplateName(String templatePath) {if (StringUtils.isEmpty(templatePath)) {return "";}if (templatePath.contains(WINDOWS_SPLIT)) {return templatePath.substring(templatePath.lastIndexOf(WINDOWS_SPLIT) + 1);}return templatePath.substring(templatePath.lastIndexOf("/") + 1);}/*** @param fileName PDF文件名    (hello.pdf)* @return 匹配到的模板名* @description 获取PDF的模板路径,* 默认按照PDF文件名匹对应模板*/public static String getPDFTemplatePath(String fileName) {String classpath = PDFKit.class.getClassLoader().getResource("").getPath();String templatePath = classpath + "/templates";File file = new File(templatePath);if (!file.isDirectory()) {throw new PDFException("PDF模板文件不存在,请检查templates文件夹!");}String pdfFileName = fileName.substring(0, fileName.lastIndexOf("."));File defaultTemplate = null;File matchTemplate = null;for (File f : file.listFiles()) {if (!f.isFile()) {continue;}String templateName = f.getName();if (templateName.lastIndexOf(".ftl") == -1) {continue;}if (defaultTemplate == null) {defaultTemplate = f;}if (StringUtils.isEmpty(fileName) && defaultTemplate != null) {break;}templateName = templateName.substring(0, templateName.lastIndexOf("."));if (templateName.toLowerCase().equals(pdfFileName.toLowerCase())) {matchTemplate = f;break;}}if (matchTemplate != null) {return matchTemplate.getAbsolutePath();}if (defaultTemplate != null) {return defaultTemplate.getAbsolutePath();}return null;}
}
package pdf.kit;import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import pdf.kit.component.PDFHeaderFooter;
import pdf.kit.component.PDFKit;
import pdf.kit.component.chart.ScatterPlotChart;
import pdf.kit.component.chart.model.XYLine;
import pdf.kit.component.chart.impl.DefaultLineChart;import java.util.ArrayList;
import java.util.List;/*** 360报告*/
@Slf4j
public class ReportKit360 {public static List<XYLine> getTemperatureLineList() {List<XYLine> list = Lists.newArrayList();for (int i = 1; i <= 7; i++) {XYLine line = new XYLine();float random = Math.round(Math.random() * 10);line.setXValue("星期" + i);line.setYValue(20 + random);line.setGroupName("下周");list.add(line);}for (int i = 1; i <= 7; i++) {XYLine line = new XYLine();float random = Math.round(Math.random() * 10);line.setXValue("星期" + i);line.setYValue(20 + random);line.setGroupName("这周");list.add(line);}return list;}public String createPDF(String templatePath, Object data, String fileName) {//pdf保存路径try {//设置自定义PDF页眉页脚工具类PDFHeaderFooter headerFooter = new PDFHeaderFooter();PDFKit kit = new PDFKit();kit.setHeaderFooterBuilder(headerFooter);//设置输出路径kit.setSaveFilePath("D:\\gitee\\pdf-kit-page\\hello.pdf");String saveFilePath = kit.exportToFile(fileName, data);return saveFilePath;} catch (Exception e) {log.error("PDF生成失败{}", ExceptionUtils.getFullStackTrace(e));return null;}}public static void main(String[] args) {ReportKit360 kit = new ReportKit360();TemplateBO templateBO = new TemplateBO();templateBO.setTemplateName("Hello iText! Hello freemarker! Hello jFreeChart!");templateBO.setFreeMarkerUrl("http://www.zheng-hang.com/chm/freemarker2_3_24/ref_directive_if.html");
//        templateBO.setITEXTUrl("http://developers.itextpdf.com/examples-itext5");
//        templateBO.setJFreeChartUrl("http://www.yiibai.com/jfreechart/jfreechart_referenced_apis.html");
//        templateBO.setImageUrl("http://mss.vip.sankuai.com/v1/mss_74e5b6ab17f44f799a524fa86b6faebf/360report/logo_1.png");List<String> scores = new ArrayList<String>();for (int i = 0; i < 200; i++) {scores.add(String.valueOf(98 + i));}templateBO.setScores(scores);//折线图
//        List<XYLine> lineList = getTemperatureLineList();
//        DefaultLineChart lineChart = new DefaultLineChart();
//        lineChart.setHeight(500);
//        lineChart.setWidth(300);
//        String picUrl = lineChart.draw(lineList, 0);
//        templateBO.setPicUrl(picUrl);//散点图
//        String scatterUrl = ScatterPlotChart.draw(ScatterPlotChartTest.getData(), 1, "他评得分(%)", "自评得分(%)");
//        templateBO.setScatterUrl(scatterUrl);String templatePath = "D:\\gitee\\pdf-kit-page\\src\\test\\resources\\templates";String path = kit.createPDF(templatePath, templateBO, "hello.pdf");System.out.println(path);}
}
package pdf.kit;import lombok.Data;import java.util.List;@Data
public class TemplateBO {private String templateName;private String freeMarkerUrl;private String ITEXTUrl;private String JFreeChartUrl;private List<String> scores;private String imageUrl;private String picUrl;private String scatterUrl;
}
package pdf.kit.exception;public class BaseException extends RuntimeException {public int errorCode;public String errorMsg;public BaseException(){super("运行时异常");}public BaseException(int errorCode, String errorMsg){super(errorMsg);this.errorCode=errorCode;this.errorMsg=errorMsg;}public BaseException(String errorMsg){super(errorMsg);this.errorCode=500;this.errorMsg=errorMsg;}public BaseException(String errorMsg, Exception e){super(errorMsg,e);this.errorCode=500;this.errorMsg=errorMsg;}
}
package pdf.kit.exception;public class FreeMarkerException extends BaseException {public FreeMarkerException(){super("FreeMarker异常");}public FreeMarkerException(int errorCode, String errorMsg){super(errorMsg);this.errorCode=errorCode;this.errorMsg=errorMsg;}public FreeMarkerException(String errorMsg){super(errorMsg);this.errorCode=500;this.errorMsg=errorMsg;}public FreeMarkerException(String errorMsg, Exception e){super(errorMsg,e);this.errorCode=500;this.errorMsg=errorMsg;}
}
package pdf.kit.exception;public class PDFException extends BaseException {public PDFException(){super("PDF异常");}public PDFException(int errorCode, String errorMsg){super(errorMsg);this.errorCode=errorCode;this.errorMsg=errorMsg;}public PDFException(String errorMsg){super(errorMsg);this.errorCode=500;this.errorMsg=errorMsg;}public PDFException(String errorMsg, Exception e){super(errorMsg,e);this.errorCode=500;this.errorMsg=errorMsg;}
}
package pdf.kit;import com.google.common.collect.Lists;
import pdf.kit.component.chart.ScatterPlotChart;
import pdf.kit.component.chart.model.XYScatter;import java.util.List;public class ScatterPlotChartTest {public static void main(String[] args) {List<XYScatter> list=getData();String path= ScatterPlotChart.draw(list,-1,"他评得分(%)","自评得分(%)");System.out.println(path);}public static List<XYScatter> getData() {List<XYScatter> list= Lists.newArrayList();XYScatter xy=new XYScatter();xy.setX(45);xy.setY(55);xy.setLabel("主见");list.add(xy);XYScatter xy1=new XYScatter();xy1.setX(75);xy1.setY(80);xy1.setLabel("定战略");list.add(xy1);XYScatter xy2=new XYScatter();xy2.setX(80);xy2.setY(80);xy2.setLabel("拿结果");list.add(xy2);XYScatter xy3=new XYScatter();xy3.setX(90);xy3.setY(25);xy3.setLabel("带团队");list.add(xy3);return list;}
}
package pdf.kit.component.chart.impl;import lombok.extern.slf4j.Slf4j;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.TextAnchor;
import pdf.kit.component.chart.LineChart;/*** 设置默认的画图工具**/
@Slf4j
public class DefaultLineChart extends LineChart {private int width;private int height;protected void initPlot(JFreeChart chart, DefaultCategoryDataset dataSet) {CategoryPlot plot = chart.getCategoryPlot();super.initDefaultXYPlot(plot);//设置节点的值显示LineAndShapeRenderer lineRender = new LineAndShapeRenderer();lineRender.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());lineRender.setBaseItemLabelsVisible(true);lineRender.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER));plot.setRenderer(lineRender);}@Overridepublic int getWidth() {if(width==0){return super.getWidth();}return width;}@Overridepublic void setWidth(int width) {this.width = width;}@Overridepublic int getHeight() {if(height==0){return super.getHeight();}return height;}@Overridepublic void setHeight(int height) {this.height = height;}
}
package pdf.kit.component.chart.model;import lombok.Data;@Data
public class XYLine {private double yValue;private String  xValue;private String groupName;public XYLine(){}public XYLine(double yValue, String xValue, String groupName){this.yValue=yValue;this.xValue=xValue;this.groupName=groupName;}
}
package pdf.kit.component.chart.model;import lombok.Data;@Data
public class XYScatter {private double x;private double y;private String label;
}
package pdf.kit.component.chart;import lombok.extern.slf4j.Slf4j;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.DefaultCategoryDataset;
import pdf.kit.component.chart.model.XYLine;
import pdf.kit.util.FontUtil;import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.List;@Slf4j
public abstract class LineChart {private  int width;private  int height;private static int   defaultWidth=500;private static int  defaultHeight=220;private String fileName;public String draw(List<XYLine> lineList, int picId){return draw("","","",lineList,picId);}public  String draw(String title, String xLabel, String yLabel,List<XYLine> lineList, int picId){if(lineList==null || lineList.size()==0){return "";}DefaultCategoryDataset dataSet=new DefaultCategoryDataset();for(XYLine line:lineList){dataSet.addValue(line.getYValue(),line.getGroupName(),line.getXValue());}try {return  drawLineChar(title,xLabel,yLabel,dataSet,picId);}catch (Exception ex){log.error("画图异常{}",ex);return "";}}/*** @description 设置自定义的线条和背景色*/protected  abstract void initPlot(JFreeChart  chart,DefaultCategoryDataset dataSet);protected void initDefaultXYPlot(CategoryPlot plot){// 设置X轴CategoryAxis domainAxis = plot.getDomainAxis();domainAxis.setLabelFont(FontUtil.getFont(Font.PLAIN,13)); // 设置横轴字体domainAxis.setTickLabelFont(FontUtil.getFont(Font.PLAIN,13));// 设置坐标轴标尺值字体domainAxis.setLowerMargin(0.01);// 左边距 边框距离domainAxis.setUpperMargin(0.06);// 右边距 边框距离,防止最后边的一个数据靠近了坐标轴。domainAxis.setMaximumCategoryLabelLines(10);domainAxis.setCategoryLabelPositions(CategoryLabelPositions.DOWN_45);// 横轴 lable 的位置 横轴上的 Lable 45度倾斜 DOWN_45// 设置Y轴NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();rangeAxis.setLabelFont(FontUtil.getFont(Font.PLAIN, 13));rangeAxis.setAutoRangeMinimumSize(1);   //最小跨度rangeAxis.setUpperMargin(0.18);//上边距,防止最大的一个数据靠近了坐标轴。rangeAxis.setLowerBound(18);   //最小值显示18rangeAxis.setUpperBound(33);  //最大值显示33rangeAxis.setAutoRange(false); //不自动分配Y轴数据rangeAxis.setTickMarkStroke(new BasicStroke(1.6f)); // 设置坐标标记大小rangeAxis.setTickMarkPaint(Color.BLACK);     // 设置坐标标记颜色rangeAxis.setTickUnit(new NumberTickUnit(1));//每1个刻度显示一个刻度值}/*** @description 画出折线图* @return 图片地址*/private  String   drawLineChar(String title, String xLabel, String yLabel, DefaultCategoryDataset dataSet, int picId) throws IOException {JFreeChart lineChartObject= ChartFactory.createLineChart(title,xLabel, // 横轴yLabel, // 纵轴dataSet, // 获得数据集PlotOrientation.VERTICAL,// 图标方向垂直true, // 显示图例false, // 不用生成工具false // 不用生成URL地址);String path=this.getClass().getClassLoader().getResource("").getPath();String filePath=path+"/images/"+picId+"/"+getFileName();File lineChart = new File(filePath);if(!lineChart.getParentFile().exists()){lineChart.getParentFile().mkdirs();}//初始化表格样式initDefaultPlot(lineChartObject,dataSet);ChartUtilities.saveChartAsJPEG(lineChart ,lineChartObject, getWidth() ,getHeight());return lineChart.getAbsolutePath();}private  void  initDefaultPlot(JFreeChart  chart,DefaultCategoryDataset dataSet){//设置公共颜色chart.getTitle().setFont(FontUtil.getFont(Font.PLAIN, 15)); // 设置标题字体chart.getLegend().setItemFont(FontUtil.getFont(Font.PLAIN, 13));// 设置图例类别字体chart.setBackgroundPaint(Color.white);// 设置背景色CategoryPlot plot = chart.getCategoryPlot();plot.setNoDataMessage("无对应的数据。");plot.setNoDataMessageFont(FontUtil.getFont(Font.PLAIN, 13));//字体的大小plot.setNoDataMessagePaint(Color.RED);//字体颜色//设置自定义颜色initPlot(chart,dataSet);}public int getWidth() {if(width==0){return defaultWidth;}return width;}public void setWidth(int width) {this.width = width;}public int getHeight() {if(height==0){return defaultHeight;}return height;}public void setHeight(int height) {this.height = height;}public String getFileName() {return fileName;}public void setFileName(String fileName) {this.fileName = fileName;}
}
package pdf.kit.component.chart;import com.google.common.collect.Lists;
import freemarker.template.utility.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYBoxAnnotation;
import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.urls.StandardXYURLGenerator;
import org.jfree.data.xy.DefaultXYDataset;
import org.jfree.data.xy.XYDataset;
import pdf.kit.component.chart.model.XYScatter;
import pdf.kit.util.FontUtil;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;/*** 散点图生成*/
@Slf4j
public class ScatterPlotChart extends ChartFactory {private static BufferedImage backgroundImage;private static String fileName="scatterChart.png";private static int width=700;private static int height=600;//上方垂直private  static double turnTop=4.712389;public static String draw(List<XYScatter> list,int picId,String xLabel,String yLabel){return draw(list,picId,"",xLabel,yLabel);}public static  String draw(List<XYScatter>  list,int picId,String title,String xLabel,String yLabel){XYDataset xyDataset= ScatterPlotChart.createXYDataSet(list,"");String path=LineChart.class.getClassLoader().getResource("").getPath();String filePath=path+"/images/"+picId+"/"+fileName;File scatterChartFile = new File(filePath);if(!scatterChartFile.getParentFile().exists()){scatterChartFile.getParentFile().mkdirs();}try {JFreeChart jFreeChart = createChart(xyDataset,list,title,xLabel,yLabel);ChartUtilities.saveChartAsJPEG(scatterChartFile ,jFreeChart, width ,height);} catch (IOException ex) {log.error("散点图生成异常:{}", ExceptionUtils.getFullStackTrace(ex));return "";}return scatterChartFile.getAbsolutePath();}public static JFreeChart createScatterPlot(String title, String xAxisLabel, String yAxisLabel, XYDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) {if(orientation == null) {throw new IllegalArgumentException("Null \'orientation\' argument.");} else {NumberAxis xAxis = new NumberAxis(xAxisLabel);xAxis.setAutoRangeIncludesZero(false);NumberAxis yAxis = new NumberAxis(yAxisLabel);yAxis.setAutoRangeIncludesZero(false);XYItemRenderer render=new StandardXYItemRenderer();render.setBaseItemLabelsVisible(false);XYPlot plot = new XYPlot(dataset, xAxis, yAxis, render);StandardXYToolTipGenerator toolTipGenerator = null;if(tooltips) {toolTipGenerator = new StandardXYToolTipGenerator();}StandardXYURLGenerator urlGenerator = null;if(urls) {urlGenerator = new StandardXYURLGenerator();}XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(false, true);renderer.setSeriesItemLabelFont(0,FontUtil.getFont(Font.PLAIN,14));renderer.setBaseItemLabelsVisible(false);renderer.setBaseToolTipGenerator(toolTipGenerator);renderer.setURLGenerator(urlGenerator);plot.setRenderer(renderer);plot.setOrientation(orientation);JFreeChart chart = new JFreeChart(title, FontUtil.getFont(Font.PLAIN,18), plot, legend);return chart;}}public static XYDataset createXYDataSet(List<XYScatter> dataList, String dataName) {DefaultXYDataset dataSet = new DefaultXYDataset();if(dataList==null ||dataList.size()==0){return dataSet;}int size = dataList.size();double[][] data = new double[2][size];for (int i = 0; i < size; i++) {XYScatter behaviorBO = dataList.get(i);double x = behaviorBO.getX();double y = behaviorBO.getY();data[0][i] = x;data[1][i] = y;}dataSet.addSeries(dataName,data);return dataSet;}public static JFreeChart createChart(XYDataset xydataset, List<XYScatter> dataList,String title,String xLabel,String yLabel) throws IOException {JFreeChart jfreechart = createScatterPlot(title,xLabel,yLabel,xydataset, PlotOrientation.VERTICAL, true, true, false);jfreechart.getLegend().setItemFont(FontUtil.getFont(Font.PLAIN,14));//去掉图例jfreechart.getLegend().setBorder(0,0,0,0);//画每个坐标点Shape shape =  new Ellipse2D.Double(-4,-4,8,8);XYPlot xyPlot = (XYPlot) jfreechart.getPlot();XYItemRenderer renderer = xyPlot.getRenderer();renderer.setItemLabelFont(FontUtil.getFont(Font.PLAIN, 14));for(int i=0;i<xydataset.getSeriesCount();i++){renderer.setSeriesShape(i, shape);renderer.setSeriesPaint(i, Color.WHITE);}//初始化背景色和坐标轴initBackGroundAndAxis(jfreechart);//区间划分initRegionPartition(jfreechart);//数据标签initDataLabels(jfreechart,dataList);return jfreechart;}/*** @description 初始化数据标签*/private static void initDataLabels(JFreeChart jfreechart, List<XYScatter> dataList) {//数据录入if(dataList==null ||dataList.size()==0){return;}XYPlot xyplot = (XYPlot) jfreechart.getPlot();for(XYScatter xyScatter:dataList){XYPointerFrameAnnotation pointerAnnotation=new XYPointerFrameAnnotation(xyScatter.getLabel(),xyScatter.getX(),xyScatter.getY(),turnTop);pointerAnnotation.setFont(FontUtil.getFont(Font.PLAIN, 13));xyplot.addAnnotation(pointerAnnotation);}}/*** @description 初始化背景色*/private static void initBackGroundAndAxis(JFreeChart jfreechart) throws IOException {jfreechart.setBackgroundPaint(Color.white);jfreechart.setBorderPaint(Color.black);jfreechart.setBorderStroke(new BasicStroke(1.5f));XYPlot xyplot = (XYPlot) jfreechart.getPlot();//无数据显示xyplot.setNoDataMessage("没有数据");xyplot.setNoDataMessageFont(FontUtil.getFont(Font.PLAIN, 18));//背景色if(backgroundImage==null){String classpath= ScatterPlotChart.class.getClassLoader().getResource("").getPath();String backgroundImg=classpath+"/background/back.png";backgroundImage = ImageIO.read(new FileInputStream(backgroundImg));}xyplot.setBackgroundImage(backgroundImage);xyplot.setBackgroundImageAlpha(1.0F);xyplot.setOutlineStroke(new BasicStroke(1.5f)); // 边框粗细ValueAxis vaaxis = xyplot.getDomainAxis();vaaxis.setAxisLineStroke(new BasicStroke(1.5f));vaaxis.setLabelFont(FontUtil.getFont(Font.PLAIN,14));//坐标轴和背景色ValueAxis va = xyplot.getDomainAxis(0);va.setAxisLineStroke(new BasicStroke(1.5f));va.setAxisLineStroke(new BasicStroke(1.5f)); // 坐标轴粗细va.setAxisLinePaint(Color.black); // 坐标轴颜色va.setLabelPaint(Color.black); // 坐标轴标题颜色va.setTickLabelPaint(Color.black); // 坐标轴标尺值颜色va.setAxisLineVisible(false);va.setLabelFont(FontUtil.getFont(Font.PLAIN, 14));ValueAxis axis = xyplot.getRangeAxis();axis.setAxisLineStroke(new BasicStroke(1.5f));XYLineAndShapeRenderer xylineandshaperenderer = (XYLineAndShapeRenderer) xyplot.getRenderer();xylineandshaperenderer.setSeriesOutlinePaint(0, Color.WHITE);xylineandshaperenderer.setUseOutlinePaint(false);//x 轴NumberAxis xAxis = (NumberAxis) xyplot.getDomainAxis();xAxis.setAutoRangeIncludesZero(false);xAxis.setAutoRangeStickyZero(false);xAxis.setTickMarkInsideLength(2.0F);xAxis.setTickMarkOutsideLength(0.0F);xAxis.setAxisLineStroke(new BasicStroke(1.5f));xAxis.setTickUnit(new NumberTickUnit(25));xAxis.setLabelFont(FontUtil.getFont(Font.PLAIN, 14));xAxis.setUpperBound(101);//最大值xAxis.setLowerBound(0);//最小值//y 轴NumberAxis yAxis = (NumberAxis) xyplot.getRangeAxis();yAxis.setAutoRangeIncludesZero(false);yAxis.setAutoRangeStickyZero(false);yAxis.setTickMarkInsideLength(2.0F);yAxis.setTickMarkOutsideLength(0.0F);yAxis.setTickUnit(new NumberTickUnit(25));yAxis.setAxisLineStroke(new BasicStroke(1.5f));yAxis.setLabelFont(FontUtil.getFont(Font.PLAIN, 14));yAxis.setUpperBound(101);//最大值yAxis.setLowerBound(0);//最小值}/*** @description 初始化区域划分* | 00 | 10  | 20 |* | 01 | 11  | 21 |* | 02 | 21  | 22 |***/private static void initRegionPartition(JFreeChart jfreechart) {Stroke stroke=new BasicStroke(0.5F);Paint outlinePaint=Color.WHITE;//划分区间XYBoxAnnotation box00 = new XYBoxAnnotation(0, 0, 50, 50,stroke,outlinePaint);XYBoxAnnotation box01 = new XYBoxAnnotation(0, 50, 50, 75,stroke,outlinePaint);XYBoxAnnotation box02 = new XYBoxAnnotation(0, 75, 50, 101,stroke,outlinePaint);XYBoxAnnotation box10 = new XYBoxAnnotation(50, 0, 75, 50,stroke,outlinePaint);XYBoxAnnotation box11 = new XYBoxAnnotation(50, 50, 75, 75,stroke,outlinePaint);XYBoxAnnotation box12 = new XYBoxAnnotation(50, 75, 75, 101,stroke,outlinePaint);XYBoxAnnotation box20 = new XYBoxAnnotation(75, 0, 101, 50,stroke,outlinePaint);XYBoxAnnotation box21 = new XYBoxAnnotation(75, 50, 101, 75,stroke,outlinePaint);XYBoxAnnotation box22 = new XYBoxAnnotation(75, 75, 101, 101,stroke,outlinePaint);XYPlot xyplot = (XYPlot) jfreechart.getPlot();xyplot.addAnnotation(box00);xyplot.addAnnotation(box01);xyplot.addAnnotation(box02);xyplot.addAnnotation(box10);xyplot.addAnnotation(box11);xyplot.addAnnotation(box12);xyplot.addAnnotation(box20);xyplot.addAnnotation(box21);xyplot.addAnnotation(box22);//添加区间标识XYTextAnnotation text1 = new XYTextAnnotation("盲区", 4, 98);text1.setFont(FontUtil.getFont(Font.PLAIN, 18));text1.setPaint(new Color(255,165,0));XYTextAnnotation text2 = new XYTextAnnotation("待发展共识区", 10, 3);text2.setFont(FontUtil.getFont(Font.PLAIN, 18));text2.setPaint(new Color(253, 88, 72));XYTextAnnotation text3 = new XYTextAnnotation("潜能区", 96, 3);text3.setFont(FontUtil.getFont(Font.PLAIN, 18));text3.setPaint(new Color(45, 139, 251));XYTextAnnotation text4 = new XYTextAnnotation("优势共识区", 93, 98);text4.setFont(FontUtil.getFont(Font.PLAIN, 18));text4.setPaint(new Color(20, 149, 134));xyplot.addAnnotation(text1);xyplot.addAnnotation(text2);xyplot.addAnnotation(text3);xyplot.addAnnotation(text4);}
}
package pdf.kit.component.chart;import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.io.SerialUtilities;
import org.jfree.text.TextUtilities;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.TextAnchor;
import org.jfree.util.ObjectUtilities;
import org.jfree.util.PublicCloneable;import java.awt.*;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;public class XYPointerFrameAnnotation extends XYTextAnnotation implements Cloneable, PublicCloneable, Serializable{private static final long serialVersionUID = -4031161445009858499L;public static final double DEFAULT_TIP_RADIUS = 10.0D;public static final double DEFAULT_BASE_RADIUS = 40.0D;public static final double DEFAULT_LABEL_OFFSET = 8.0D;public static final double DEFAULT_ARROW_LENGTH = 10.0D;public static final double DEFAULT_ARROW_WIDTH = 3.0D;private double angle;private double tipRadius;private double baseRadius;private double arrowLength;private double arrowWidth;private transient Stroke arrowStroke;private transient Paint arrowPaint;private double labelOffset;private transient Paint linePaint;public XYPointerFrameAnnotation(String label, double x, double y, double angle) {super(label, x, y);this.angle = angle;this.tipRadius = 1;//连接线到点的距离this.baseRadius =50;//连接线的半径this.arrowLength =0;//箭头长度 不需要this.arrowWidth = 0;//箭头宽度 不需要this.labelOffset = 10;//label字体和竖线的距离this.arrowStroke = new BasicStroke(1.0F);this.arrowPaint = Color.WHITE;this.linePaint=Color.WHITE;}public double getAngle() {return this.angle;}public void setAngle(double angle) {this.angle = angle;}public double getTipRadius() {return this.tipRadius;}public void setTipRadius(double radius) {this.tipRadius = radius;}public double getBaseRadius() {return this.baseRadius;}public void setBaseRadius(double radius) {this.baseRadius = radius;}public double getLabelOffset() {return this.labelOffset;}public void setLabelOffset(double offset) {this.labelOffset = offset;}public double getArrowLength() {return this.arrowLength;}public void setArrowLength(double length) {this.arrowLength = length;}public double getArrowWidth() {return this.arrowWidth;}public void setArrowWidth(double width) {this.arrowWidth = width;}public Stroke getArrowStroke() {return this.arrowStroke;}public Paint getLinePaint() {return linePaint;}public void setLinePaint(Paint linePaint) {this.linePaint = linePaint;}public void setArrowStroke(Stroke stroke) {if(stroke == null) {throw new IllegalArgumentException("Null \'stroke\' not permitted.");} else {this.arrowStroke = stroke;}}public Paint getArrowPaint() {return this.arrowPaint;}public void setArrowPaint(Paint paint) {this.arrowPaint = paint;}public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) {PlotOrientation orientation = plot.getOrientation();RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(plot.getDomainAxisLocation(), orientation);RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(plot.getRangeAxisLocation(), orientation);double j2DX = domainAxis.valueToJava2D(this.getX(), dataArea, domainEdge);double j2DY = rangeAxis.valueToJava2D(this.getY(), dataArea, rangeEdge);double startX;if(orientation == PlotOrientation.HORIZONTAL) {startX = j2DX;j2DX = j2DY;j2DY = startX;}startX = j2DX + Math.cos(this.angle) * this.baseRadius;double startY = j2DY + Math.sin(this.angle) * this.baseRadius;double endX = j2DX + Math.cos(this.angle) * this.tipRadius;double endY = j2DY + Math.sin(this.angle) * this.tipRadius;g2.setStroke(this.arrowStroke);g2.setPaint(this.arrowPaint);Line2D.Double line = new Line2D.Double(startX, startY, endX, endY);g2.draw(line);g2.setFont(this.getFont());g2.setPaint(this.getPaint());double labelX = j2DX + Math.cos(this.angle) * (this.baseRadius + this.labelOffset);double labelY = j2DY + Math.sin(this.angle) * (this.baseRadius + this.labelOffset);Rectangle2D rectangle2D=TextUtilities.drawAlignedString(this.getText(), g2,(float)labelX, (float)labelY, TextAnchor.BASELINE_CENTER);//添加边框g2.setColor(Color.WHITE);g2.setStroke(new BasicStroke(1.0F));rectangle2D.setFrame(rectangle2D.getX()-3,rectangle2D.getY()-2,rectangle2D.getWidth()+5,rectangle2D.getHeight()+2);g2.draw(rectangle2D);}public boolean equals(Object obj) {if(obj == null) {return false;} else if(obj == this) {return true;} else if(!(obj instanceof XYPointerFrameAnnotation)) {return false;} else if(!super.equals(obj)) {return false;} else {XYPointerFrameAnnotation that = (XYPointerFrameAnnotation)obj;return this.angle != that.angle?false:(this.tipRadius != that.tipRadius?false:(this.baseRadius != that.baseRadius?false:(this.arrowLength != that.arrowLength?false:(this.arrowWidth != that.arrowWidth?false:(!this.arrowPaint.equals(that.arrowPaint)?false:(!ObjectUtilities.equal(this.arrowStroke, that.arrowStroke)?false:this.labelOffset == that.labelOffset))))));}}public Object clone() throws CloneNotSupportedException {return super.clone();}private void writeObject(ObjectOutputStream stream) throws IOException {stream.defaultWriteObject();SerialUtilities.writePaint(this.arrowPaint, stream);SerialUtilities.writeStroke(this.arrowStroke, stream);}private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {stream.defaultReadObject();this.arrowPaint = SerialUtilities.readPaint(stream);this.arrowStroke = SerialUtilities.readStroke(stream);}
}

freemarker转PDF,分页,页眉和页脚,画图相关推荐

  1. iText + Freemarker实现pdf的导出,支持页眉添加图片,及其他的基础功能

    本文为了记录自己的实现过程,具体的实现步骤是参考博文 https://www.cnblogs.com/youzhibing/p/7692366.html 来实现的,只是在他的基础上添加了页眉页脚及页眉 ...

  2. python使用fpdf创建pdf文件包含:页眉、页脚并嵌入logo图片、设置使用中文字体

    python使用fpdf创建pdf文件包含:页眉.页脚并嵌入logo图片.设置使用中文字体 #python使用fpdf创建页眉.页脚并嵌入logo图片.设置使用中文字体 from fpdf impor ...

  3. python使用fpdf生成pdf文件章节(chapter),包含:页眉、页脚、章节主题、数据排版等;

    python使用fpdf生成pdf文件章节(chapter),包含:页眉.页脚.章节主题.数据排版等: #仿真数据 The year 1866 was marked by a bizarre deve ...

  4. python使用fpdf生成pdf章节(chapter)文件包含:页眉、页脚、章节主体、章节内容等;

    python使用fpdf生成pdf章节(chapter)文件包含:页眉.页脚.章节主体.章节内容等: 目录

  5. jodconverter水印java,OpenOffice实现Office转Pdf(支持自定义添加水印、页眉、页脚)

    java OpenOffice officetopdf 最近项目需要实现下载Office文档时自动转成PDF文档,以下代码支持2003及2007版的Word,PPT,Excel转换,并支持自定义添加水 ...

  6. java中生成pdf,插入图片,页眉、页脚、表格

    全栈工程师开发手册 (作者:栾鹏) java教程全解 java中生成pdf,插入图片,页眉.页脚.表格 import com.lowagie.text.*; import com.lowagie.te ...

  7. Tcpdf操作html转pdf带页眉和页脚

    一:安装Tcpdf依赖 composer install tecnickcom/tcpdf 二:编写htmlToPdf方法 /******* html生成pdf文件* tag: 合约生成* $para ...

  8. php mpdf导航栏信息,php – 我想使用mpdf在pdf中设置页眉和页脚

    我在Codeigniter中使用mpdf库生成一个PDF. 我想以适当的边距附加标题图像和页脚图像,我创建了一个代码,但页眉和页脚重叠请帮我解决这个问题 提前致谢 //controller $this ...

  9. itext pdf 加页眉和页脚加页数(二)

    新建class: import java.io.IOException; import com.itextpdf.text.Document; import com.itextpdf.text.Doc ...

  10. pdf实现页眉或者页脚代码

    pdf实现页眉或者页脚代码 该类实现 package com.tj.printer.web.printer; import com.itextpdf.text.*; import com.itextp ...

最新文章

  1. [文摘20070307]英语表示“走”的20种形式
  2. pythonweb服务器部署iis_Python开发入门与实战15-IIS部署
  3. 后缀树和后缀数组的一些资料收集
  4. hadoop2.2.0 分布式存储hdfs完全分布式搭建及功能测试记录(一)----架构及原理介绍...
  5. mac识别androidUSB调试
  6. ThunderNet:国防科大、旷视提出首个在ARM上实时运行的通用目标检测算法
  7. 如何优化网站的响应时间
  8. 进程/线程间的同步方式
  9. 计蒜客 - 守望者的逃离
  10. 实战react技术栈+express前后端博客项目(3)-- 后端路由、代理以及静态资源托管等配置说明...
  11. 计算机网络实验|DNS 域名服务协议
  12. 树莓派+aria2+yaaw搭建下载机
  13. 王石:中国楼市泡沫必破 目前只是时间问题
  14. 数学-向量公式总结和一些公式证明
  15. 【临床预测模型】----选择合适的统计模型
  16. 美团App用户界面分析
  17. 2022年系统集成企业的物联网平台首选:物联集成平台
  18. Scratch制作俄罗斯方块消除游戏
  19. 微软老照片AI修复开源代码运行错误
  20. 萧乾升:3.26黄金原油最新行情,让我们牛转乾坤

热门文章

  1. centos 7 sogou input
  2. 计算机教师个人能力不足,教师个人信息技术能力分析优势和不足.docx
  3. (转)Windows 7 with SP1中英文原版MSDN下载汇总(全版本收录完毕)
  4. 八种实用的免费游戏开发软件工具
  5. 2022中山大学计算机技术专硕考研初试、复试经验帖
  6. 蓝牙抓包工具选择android,Android 蓝牙抓包
  7. Linux的初学者写代码基本步骤(make、gcc和gdb)
  8. MATLAB切换中文(无语言选项)
  9. 标定工具CANape软件使用之新建工程
  10. android编写计算器代码,Android入门计算器编写代码