900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > SpringBoot html转pdf 支持中文 图片水印+文字水印 页眉页脚 flying-saucer-pdf-itext5 + freemarker

SpringBoot html转pdf 支持中文 图片水印+文字水印 页眉页脚 flying-saucer-pdf-itext5 + freemarker

时间:2019-03-03 23:42:32

相关推荐

SpringBoot html转pdf 支持中文 图片水印+文字水印 页眉页脚 flying-saucer-pdf-itext5 + freemarker

使用flying-saucer-pdf-itext5加freemarker生成pdf,支持中文、图片水印+文字水印、页眉页脚。

引入jar包

<!-- freemarker --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency><!-- flying-saucer html转pdf--><dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf-itext5</artifactId><version>9.1.20</version></dependency><!-- jsoup HTML parser --><dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.13.1</version></dependency>

PDF转换工具类:

import com.itextpdf.text.DocumentException;import com.itextpdf.text.pdf.BaseFont;import freemarker.template.Configuration;import freemarker.template.Template;import freemarker.template.TemplateException;import freemarker.template.TemplateExceptionHandler;import mons.lang3.StringUtils;import org.springframework.core.io.ClassPathResource;import org.xhtmlrenderer.pdf.ITextFontResolver;import org.xhtmlrenderer.util.XRRuntimeException;import java.io.*;import java.util.Map;/*** 导出PDF工具类*/public class PDFUtil {private PDFUtil() {}private volatile static Configuration configuration;static {if (configuration == null) {synchronized (PDFUtil.class) {if (configuration == null) {configuration = new Configuration(Configuration.VERSION_2_3_28);}}}}/*** freemarker 通过流的方式 引擎渲染 html** @param dataMap传入 html 模板的 Map 数据* @param ftlFilePath html 模板文件相对路径(相对于 resources路径,路径 + 文件名),之后通过 BufferedReader 流来读取模板*eg: "templates/pdf_export_demo.ftl"* @return*/public static String freemarkerRender(Map<String, Object> dataMap, String ftlFilePath) {configuration.setDefaultEncoding("UTF-8");configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);configuration.setLogTemplateExceptions(false);configuration.setWrapUncheckedExceptions(true);Writer out = new StringWriter();try {Template template = new Template("", PDFUtil.returnReaderStream(ftlFilePath), configuration);template.process(dataMap, out);out.flush();return out.toString();} catch (IOException | TemplateException e) {e.printStackTrace();} finally {try {out.close();} catch (IOException e) {e.printStackTrace();}}return null;}/*** 使用 iText 生成 PDF 文档** @param htmlTmpStr html 模板文件字符串* @param fontFilePath 所需字体文件(相对路径+文件名)* @param waterImgPath 水印图片路径* @param waterContent 水印文字内容*/public static byte[] createPDF(String htmlTmpStr, String fontFilePath, String waterImgPath, String waterContent) {ByteArrayOutputStream outputStream = null;byte[] result = null;try {outputStream = new ByteArrayOutputStream();ITextRendererO renderer = new ITextRendererO();renderer.setDocumentFromString(htmlTmpStr);ITextFontResolver fontResolver = renderer.getFontResolver();// 解决中文支持问题,需要所需字体(ttc)文件fontResolver.addFont(fontFilePath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);renderer.layout();renderer.createPDF(outputStream, fontFilePath, waterImgPath, waterContent);renderer.finishPDF();result = outputStream.toByteArray();} catch (IOException | XRRuntimeException | DocumentException e) {e.printStackTrace();return null;} finally {try {if (outputStream != null) {outputStream.close();}} catch (IOException e) {e.printStackTrace();}}return result;}/*** 根据文件相对路径返回一个 BufferedReader 流** @param filePath* @return* @throws IOException*/public static BufferedReader returnReaderStream(String filePath) throws IOException {return new BufferedReader(new InputStreamReader(new ClassPathResource(filePath).getInputStream()));}/*** 根据文件相对路径返回一个 BufferedReader 流** @param filePath* @return* @throws IOException*/public static InputStream returnInputStream(String filePath) throws IOException {return new ClassPathResource(filePath).getInputStream();}}

水印页眉页码生成:

重写ITextRenderer类

import com.itextpdf.text.DocumentException;import com.itextpdf.text.pdf.PdfWriter;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.Node;import org.xhtmlrenderer.context.StyleReference;import org.xhtmlrenderer.css.style.CalculatedStyle;import org.xhtmlrenderer.css.style.derived.RectPropertySet;import org.xhtmlrenderer.extend.NamespaceHandler;import org.xhtmlrenderer.extend.UserInterface;import org.xhtmlrenderer.layout.BoxBuilder;import org.xhtmlrenderer.layout.Layer;import org.xhtmlrenderer.layout.LayoutContext;import org.xhtmlrenderer.layout.SharedContext;import org.xhtmlrenderer.pdf.*;import org.xhtmlrenderer.render.BlockBox;import org.xhtmlrenderer.render.PageBox;import org.xhtmlrenderer.render.RenderingContext;import org.xhtmlrenderer.render.ViewportBox;import org.xhtmlrenderer.resource.XMLResource;import org.xhtmlrenderer.simple.extend.XhtmlNamespaceHandler;import org.xhtmlrenderer.util.Configuration;import org.xml.sax.InputSource;import javax.xml.transform.*;import javax.xml.transform.dom.DOMSource;import javax.xml.transform.stream.StreamResult;import java.awt.*;import java.io.*;import java.util.List;import java.util.regex.Pattern;public class ITextRendererO extends ITextRenderer {// These two defaults combine to produce an effective resolution of 96 px to// the inchprivate static final float DEFAULT_DOTS_PER_POINT = 20f * 4f / 3f;private static final int DEFAULT_DOTS_PER_PIXEL = 20;private final SharedContext _sharedContext;private final ITextOutputDevice _outputDevice;private Document _doc;private BlockBox _root;private final float _dotsPerPoint;private com.itextpdf.text.Document _pdfDoc;private PdfWriter _writer;private PDFEncryption _pdfEncryption;// note: not hard-coding a default version in the _pdfVersion field as this// may change between iText releases// check for null before calling writer.setPdfVersion()// use one of the values in PDFWriter.VERSION...private Character _pdfVersion;private Dimension _dim;private boolean scaleToFit;private final char[] validPdfVersions = new char[]{PdfWriter.VERSION_1_2, PdfWriter.VERSION_1_3, PdfWriter.VERSION_1_4,PdfWriter.VERSION_1_5, PdfWriter.VERSION_1_6, PdfWriter.VERSION_1_7};private PDFCreationListener _listener;public ITextRendererO() {this(DEFAULT_DOTS_PER_POINT, DEFAULT_DOTS_PER_PIXEL);}public ITextRendererO(float dotsPerPoint, int dotsPerPixel) {_dotsPerPoint = dotsPerPoint;_outputDevice = new ITextOutputDevice(_dotsPerPoint);ITextUserAgent userAgent = new ITextUserAgent(_outputDevice);_sharedContext = new SharedContext();_sharedContext.setUserAgentCallback(userAgent);_sharedContext.setCss(new StyleReference(userAgent));userAgent.setSharedContext(_sharedContext);_outputDevice.setSharedContext(_sharedContext);ITextFontResolver fontResolver = new ITextFontResolver(_sharedContext);_sharedContext.setFontResolver(fontResolver);ITextReplacedElementFactory replacedElementFactory = new ITextReplacedElementFactory(_outputDevice);_sharedContext.setReplacedElementFactory(replacedElementFactory);_sharedContext.setTextRenderer(new ITextTextRenderer());_sharedContext.setDPI(72 * _dotsPerPoint);_sharedContext.setDotsPerPixel(dotsPerPixel);_sharedContext.setPrint(true);_sharedContext.setInteractive(false);}public Document getDocument() {return _doc;}public ITextFontResolver getFontResolver() {return (ITextFontResolver) _sharedContext.getFontResolver();}private Document loadDocument(final String uri) {return _sharedContext.getUac().getXMLResource(uri).getDocument();}public void setDocument(String uri) {setDocument(loadDocument(uri), uri);}public void setDocument(Document doc, String url) {setDocument(doc, url, new XhtmlNamespaceHandler());}public void setDocument(File file) throws IOException {File parent = file.getAbsoluteFile().getParentFile();setDocument(loadDocument(file.toURI().toURL().toExternalForm()), (parent == null ? "" : parent.toURI().toURL().toExternalForm()));}public void setDocument(byte[] bytes) throws IOException {ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);Document dom = XMLResource.load(inputStream).getDocument();setDocument(dom, "");}public void setDocumentFromString(String content) {setDocumentFromString(content, null);}public void setDocumentFromString(String content, String baseUrl) {InputSource is = new InputSource(new BufferedReader(new StringReader(content)));Document dom = XMLResource.load(is).getDocument();setDocument(dom, baseUrl);}public void setDocument(Document doc, String url, NamespaceHandler nsh) {_doc = doc;getFontResolver().flushFontFaceFonts();_sharedContext.reset();if (Configuration.isTrue("xr.cache.stylesheets", true)) {_sharedContext.getCss().flushStyleSheets();} else {_sharedContext.getCss().flushAllStyleSheets();}_sharedContext.setBaseURL(url);_sharedContext.setNamespaceHandler(nsh);_sharedContext.getCss().setDocumentContext(_sharedContext, _sharedContext.getNamespaceHandler(), doc, new NullUserInterface());getFontResolver().importFontFaces(_sharedContext.getCss().getFontFaceRules());}public PDFEncryption getPDFEncryption() {return _pdfEncryption;}public void setPDFEncryption(PDFEncryption pdfEncryption) {_pdfEncryption = pdfEncryption;}public void setPDFVersion(char _v) {for (int i = 0; i < validPdfVersions.length; i++) {if (_v == validPdfVersions[i]) {_pdfVersion = new Character(_v);return;}}throw new IllegalArgumentException("Invalid PDF version character; use "+ "valid constants from PdfWriter (e.g. PdfWriter.VERSION_1_2)");}public char getPDFVersion() {return _pdfVersion == null ? '0' : _pdfVersion.charValue();}public void layout() {LayoutContext c = newLayoutContext();BlockBox root = BoxBuilder.createRootBox(c, _doc);root.setContainingBlock(new ViewportBox(getInitialExtents(c)));root.layout(c);_dim = root.getLayer().getPaintingDimension(c);root.getLayer().trimEmptyPages(c, _dim.height);root.getLayer().layoutPages(c);_root = root;}private Rectangle getInitialExtents(LayoutContext c) {PageBox first = Layer.createPageBox(c, "first");return new Rectangle(0, 0, first.getContentWidth(c), first.getContentHeight(c));}private RenderingContext newRenderingContext() {RenderingContext result = _sharedContext.newRenderingContextInstance();result.setFontContext(new ITextFontContext());result.setOutputDevice(_outputDevice);_sharedContext.getTextRenderer().setup(result.getFontContext());result.setRootLayer(_root.getLayer());return result;}private LayoutContext newLayoutContext() {LayoutContext result = _sharedContext.newLayoutContextInstance();result.setFontContext(new ITextFontContext());_sharedContext.getTextRenderer().setup(result.getFontContext());return result;}public void writeNextDocument() throws DocumentException, IOException {writeNextDocument(0);}public void writeNextDocument(int initialPageNo) throws DocumentException, IOException {List pages = _root.getLayer().getPages();RenderingContext c = newRenderingContext();c.setInitialPageNo(initialPageNo);PageBox firstPage = (PageBox) pages.get(0);com.itextpdf.text.Rectangle firstPageSize = new com.itextpdf.text.Rectangle(0, 0, firstPage.getWidth(c) / _dotsPerPoint,firstPage.getHeight(c) / _dotsPerPoint);_outputDevice.setStartPageNo(_writer.getPageNumber());_pdfDoc.setPageSize(firstPageSize);_pdfDoc.newPage();writePDF(pages, c, firstPageSize, _pdfDoc, _writer);}public void finishPDF() {if (_pdfDoc != null) {fireOnClose();_pdfDoc.close();}}/*** @param os pdf输出流* @param fontFilePath 文字文件路径* @param waterImgPath 水印图片路径* @param waterContent 水印内容* @throws DocumentException*/public void createPDF(OutputStream os, String fontFilePath, String waterImgPath, String waterContent) throws DocumentException, IOException {createPDF(os, true, 0, fontFilePath, waterImgPath, waterContent);}/*** <B>NOTE:</B> Caller is responsible for cleaning up the OutputStream if* something goes wrong.** @throws IOException*/public void createPDF(OutputStream os, boolean finish, int initialPageNo, String fontFilePath, String waterImgPath, String waterContent) throws DocumentException, IOException {List pages = _root.getLayer().getPages();RenderingContext c = newRenderingContext();c.setInitialPageNo(initialPageNo);PageBox firstPage = (PageBox) pages.get(0);int pageWidth = calculateWidth(c, firstPage);com.itextpdf.text.Rectangle firstPageSize = new com.itextpdf.text.Rectangle(0, 0, pageWidth / _dotsPerPoint,firstPage.getHeight(c) / _dotsPerPoint);com.itextpdf.text.Document doc = new com.itextpdf.text.Document(firstPageSize, 0, 0, 0, 0);PdfWriter writer = PdfWriter.getInstance(doc, os);// 添加水印和页码PDFBuilder builder = new PDFBuilder(fontFilePath , waterImgPath, waterContent);writer.setPageEvent(builder);if (_pdfVersion != null) {writer.setPdfVersion(_pdfVersion.charValue());}if (_pdfEncryption != null) {writer.setEncryption(_pdfEncryption.getUserPassword(), _pdfEncryption.getOwnerPassword(),_pdfEncryption.getAllowedPrivileges(), _pdfEncryption.getEncryptionType());}_pdfDoc = doc;_writer = writer;firePreOpen();doc.open();writePDF(pages, c, firstPageSize, doc, writer);if (finish) {fireOnClose();doc.close();}}private int calculateWidth(RenderingContext c, PageBox firstPage) {if (isScaleToFit()) {int pageWidth = firstPage.getWidth(c);Rectangle pageRec = firstPage.getPrintClippingBounds(c);if (_dim.getWidth() > pageRec.getWidth()) {RectPropertySet margin = firstPage.getMargin(c);pageWidth = (int) (_dim.getWidth() + margin.left() + margin.right());}return pageWidth;} else {return firstPage.getWidth(c);}}private void firePreOpen() {if (_listener != null) {_listener.preOpen(this);}}private void firePreWrite(int pageCount) {if (_listener != null) {_listener.preWrite(this, pageCount);}}private void fireOnClose() {if (_listener != null) {_listener.onClose(this);}}private void writePDF(List pages, RenderingContext c, com.itextpdf.text.Rectangle firstPageSize, com.itextpdf.text.Document doc,PdfWriter writer) throws DocumentException, IOException {_outputDevice.setRoot(_root);_outputDevice.start(_doc);_outputDevice.setWriter(writer);_outputDevice.initializePage(writer.getDirectContent(), firstPageSize.getHeight());_root.getLayer().assignPagePaintingPositions(c, Layer.PAGED_MODE_PRINT);int pageCount = _root.getLayer().getPages().size();c.setPageCount(pageCount);firePreWrite(pageCount); // opportunity to adjust meta datasetDidValues(doc); // set PDF header fields from meta datafor (int i = 0; i < pageCount; i++) {PageBox currentPage = (PageBox) pages.get(i);c.setPage(i, currentPage);paintPage(c, writer, currentPage);_outputDevice.finishPage();if (i != pageCount - 1) {PageBox nextPage = (PageBox) pages.get(i + 1);int pageWidth = calculateWidth(c, nextPage);com.itextpdf.text.Rectangle nextPageSize = new com.itextpdf.text.Rectangle(0, 0, pageWidth / _dotsPerPoint,nextPage.getHeight(c) / _dotsPerPoint);// com.itextpdf.text.Rectangle nextPageSize = new com.itextpdf.text.Rectangle(0, 0, nextPage.getWidth(c) / _dotsPerPoint,// nextPage.getHeight(c) / _dotsPerPoint);doc.setPageSize(nextPageSize);doc.newPage();_outputDevice.initializePage(writer.getDirectContent(), nextPageSize.getHeight());}}_outputDevice.finish(c, _root);}// Sets the document information dictionary values from html metadataprivate void setDidValues(com.itextpdf.text.Document doc) {String v = _outputDevice.getMetadataByName("title");if (v != null) {doc.addTitle(v);}v = _outputDevice.getMetadataByName("author");if (v != null) {doc.addAuthor(v);}v = _outputDevice.getMetadataByName("subject");if (v != null) {doc.addSubject(v);}v = _outputDevice.getMetadataByName("keywords");if (v != null) {doc.addKeywords(v);}}private void paintPage(RenderingContext c, PdfWriter writer, PageBox page) throws IOException {provideMetadataToPage(writer, page);page.paintBackground(c, 0, Layer.PAGED_MODE_PRINT);page.paintMarginAreas(c, 0, Layer.PAGED_MODE_PRINT);page.paintBorder(c, 0, Layer.PAGED_MODE_PRINT);Shape working = _outputDevice.getClip();Rectangle content = page.getPrintClippingBounds(c);if (isScaleToFit()) {int pageWidth = calculateWidth(c, page);content.setSize(pageWidth, (int) content.getSize().getHeight());//RTD - to change}_outputDevice.clip(content);int top = -page.getPaintingTop() + page.getMarginBorderPadding(c, CalculatedStyle.TOP);int left = page.getMarginBorderPadding(c, CalculatedStyle.LEFT);_outputDevice.translate(left, top);_root.getLayer().paint(c);_outputDevice.translate(-left, -top);_outputDevice.setClip(working);}private void provideMetadataToPage(PdfWriter writer, PageBox page) throws IOException {byte[] metadata = null;if (page.getMetadata() != null) {try {String metadataBody = stringfyMetadata(page.getMetadata());if (metadataBody != null) {metadata = createXPacket(stringfyMetadata(page.getMetadata())).getBytes("UTF-8");}} catch (UnsupportedEncodingException e) {// Can't happenthrow new RuntimeException(e);}}if (metadata != null) {writer.setPageXmpMetadata(metadata);}}private String stringfyMetadata(Element element) {Element target = getFirstChildElement(element);if (target == null) {return null;}try {TransformerFactory factory = TransformerFactory.newInstance();Transformer transformer = factory.newTransformer();transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");StringWriter output = new StringWriter();transformer.transform(new DOMSource(target), new StreamResult(output));return output.toString();} catch (TransformerConfigurationException e) {// Things must be in pretty bad shape to get here so// rethrow as runtime exceptionthrow new RuntimeException(e);} catch (TransformerException e) {throw new RuntimeException(e);}}private static Element getFirstChildElement(Element element) {Node n = element.getFirstChild();while (n != null) {if (n.getNodeType() == Node.ELEMENT_NODE) {return (Element) n;}n = n.getNextSibling();}return null;}private String createXPacket(String metadata) {StringBuffer result = new StringBuffer(metadata.length() + 50);result.append("<?xpacket begin='\uFEFF' id='W5M0MpCehiHzreSzNTczkc9d'?>\n");result.append(metadata);result.append("\n<?xpacket end='r'?>");return result.toString();}public ITextOutputDevice getOutputDevice() {return _outputDevice;}public SharedContext getSharedContext() {return _sharedContext;}public void exportText(Writer writer) throws IOException {RenderingContext c = newRenderingContext();c.setPageCount(_root.getLayer().getPages().size());_root.exportText(c, writer);}public BlockBox getRootBox() {return _root;}public float getDotsPerPoint() {return _dotsPerPoint;}public List findPagePositionsByID(Pattern pattern) {return _outputDevice.findPagePositionsByID(newLayoutContext(), pattern);}private static final class NullUserInterface implements UserInterface {public boolean isHover(Element e) {return false;}public boolean isActive(Element e) {return false;}public boolean isFocus(Element e) {return false;}}public PDFCreationListener getListener() {return _listener;}public void setListener(PDFCreationListener listener) {_listener = listener;}public PdfWriter getWriter() {return _writer;}public boolean isScaleToFit() {return scaleToFit;}public boolean setScaleToFit(boolean scaleToFit) {return this.scaleToFit = scaleToFit;}}

生成水印和页眉页码的事件

package cn.cooptec.admin.core.utils;import com.itextpdf.text.*;import com.itextpdf.text.pdf.*;import mons.io.IOUtils;import mons.lang3.StringUtils;import java.io.IOException;import java.io.InputStream;/*** 设置页面附加属性*/public class PDFBuilder extends PdfPageEventHelper {/*** 页眉描述*/private String header = "我是页眉";/*** 水印图片地址*/private String waterImgPath;/*** 水印内容*/private String waterContent;/*** 文档字体大小,页脚页眉最好和文本大小一致*/private int presentFontSize = 12;// 基础字体对象private BaseFont bf = null;// 利用基础字体生成的字体对象,一般用于生成中文文字private Font fontDetail = null;/*** Creates a new instance of PdfReportM1HeaderFooter 无参构造方法.*/public PDFBuilder() {}public PDFBuilder(String fontFilePath, String waterImgPath, String waterContent) {try {if (StringUtils.isNotBlank(fontFilePath)) {// 设置文字中文字体this.bf = BaseFont.createFont(fontFilePath + ",1", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, false);this.fontDetail = new Font(this.bf, presentFontSize, Font.NORMAL);} else {//设置分页页眉页脚字体if (bf == null) {bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);}if (fontDetail == null) {fontDetail = new Font(bf, presentFontSize, Font.NORMAL); // 数据体字体}}} catch (IOException | DocumentException e) {e.printStackTrace();}this.waterImgPath = waterImgPath;this.waterContent = waterContent;}public void setHeader(String header) {this.header = header;}public void setPresentFontSize(int presentFontSize) {this.presentFontSize = presentFontSize;}/*** 文档打开时*/@Overridepublic void onOpenDocument(PdfWriter writer, Document document) {}/*** 关闭每页的时候,写入页眉,写入'第几页'这几个字。** @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(com.itextpdf.text.pdf.PdfWriter,* com.itextpdf.text.Document)*/@Overridepublic void onEndPage(PdfWriter writer, Document document) {this.addPageHeaderFooter(writer, document);this.addWatermark(writer, document);}// 添加页眉页脚private void addPageHeaderFooter(PdfWriter writer, Document document) {int pageS = writer.getPageNumber();if (pageS != 1) { // 封面不展示页眉页脚pageS = pageS - 1;float len1 = bf.getWidthPoint(header, presentFontSize);// 1.写入页眉ColumnText.showTextAligned(writer.getDirectContent(),Element.ALIGN_CENTER, new Phrase(header, fontDetail),(document.rightMargin() + document.right() + document.leftMargin() - document.left() - len1) / 2.0F + 23F, document.top() - 25, 0);// 2.写入页脚// 2.写入 第 X 页String foot = "第 " + pageS + " 页";Phrase footer = new Phrase(foot, fontDetail);// 3.计算前半部分的foot1的长度,后面好定位最后一部分的'Y页'这俩字的x轴坐标,字体长度也要计算进去 = lenfloat len = bf.getWidthPoint(foot, presentFontSize);// 4.拿到当前的PdfContentBytePdfContentByte cb = writer.getDirectContent();// 5.写入页脚1,x轴就是(右margin+左margin + right() -left()- len)/2.0F// 再给偏移20F适合人类视觉感受,否则肉眼看上去就太偏左了// ,y轴就是底边界-20,否则就贴边重叠到数据体里了就不是页脚了;注意Y轴是从下往上累加的,最上方的Top值是大于Bottom好几百开外的。ColumnText.showTextAligned(cb,Element.ALIGN_CENTER,footer,(document.rightMargin() + document.right() + document.leftMargin() - document.left() - len) / 2.0F + 20F,document.bottom() + 15, 0);}}// 添加水印public void addWatermark(PdfWriter writer, Document document) {try {// 水印图片Image waterImg = null;if (StringUtils.isNotBlank(waterImgPath)) {InputStream inputStream = PDFUtil.returnInputStream(waterImgPath);waterImg = Image.getInstance(IOUtils.toByteArray(inputStream));}PdfContentByte pdfContent = writer.getDirectContent();// 开始写入水印pdfContent.beginText();// 设置水印透明度PdfGState gs = new PdfGState();// 水印透明度gs.setFillOpacity(1f);pdfContent.setGState(gs);// 文字水印pdfContent.setColorFill(BaseColor.LIGHT_GRAY);// 字体大小pdfContent.setFontAndSize(bf, presentFontSize);// showTextAligned 方法的参数分别是(文字对齐方式,位置内容,输出水印X轴位置,Y轴位置,旋转角度)pdfContent.showTextAligned(Element.ALIGN_LEFT, waterContent, document.right() - 170, document.bottom() + 15, 0);int length = waterContent.length();// 图片水印if (waterImg != null) {// 设置坐标 绝对位置 X YwaterImg.setAbsolutePosition(document.right() - (180 + length), document.bottom() + 10);// 设置旋转弧度waterImg.setRotation(0);// 旋转 弧度// 设置旋转角度waterImg.setRotationDegrees(0);// 旋转 角度// 设置等比缩放waterImg.scaleAbsolute(20, 20);// 自定义大小// 写入图片水印pdfContent.addImage(waterImg);}// 结束写入水印pdfContent.endText();} catch (IOException | DocumentException e) {e.printStackTrace();}}/*** 关闭文档时*/public void onCloseDocument(PdfWriter writer, Document document) {}}

调用:

/*** 字体文件相对路径*/@Value("${pdfExport.font}")private String pdfFont;/*** 导出PDF模板文件相对路径*/@Value("${pdfExport.pdfTemplate}")private String pdfTemplate;/*** 水印图片路径*/@Value("${pdfExport.waterImg}")private String waterImg;public void test(){byte[] pdfBytes = PDFUtil.createPDF(htmlToPdfHtml(logHtml), pdfFont, waterImg, "我是水印");}// 将html转为pdf模板private String htmlToPdfHtml(String html) {// 将html格式转为xhtml,自动添加闭合Document doc = Jsoup.parse(html);doc.outputSettings().syntax(Document.OutputSettings.Syntax.xml).escapeMode(Entities.EscapeMode.xhtml);html = doc.body().html();// 过滤换行和 html 空格 (&nbsp;非标准 html)html = fixSpace(html);// 过滤特殊 unicode 字符html = stripNonValidXMLCharacters(html);Map<String, Object> dataMap = new HashMap<>();dataMap.put("content", html);// freemarker模板转HTMLreturn PDFUtil.freemarkerRender(dataMap, pdfTemplate);}// 过滤无效字符private String stripNonValidXMLCharacters(String in) {StringBuilder out = new StringBuilder(); // Used to hold the output.char current; // Used to reference the current character.if (in == null || ("".equals(in)))return ""; // vacancy test.for (int i = 0; i < in.length(); i++) {current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught// here; it should not happen.if (current == 0x9 || current == 0xA || current == 0xD || current >= 0x20 && current <= 0xD7FF || current >= 0xE000 && current <= 0xFFFD)out.append(current);}return out.toString();}// 过滤换行和 html 空格 (&nbsp;非标准 html)private String fixSpace(String html) {final String pattern = "&(\\s*)nbsp;|\\n";return html.replaceAll(pattern, " ");}

配置文件:

# pdf export configpdfExport:font: "static/fonts/simsun.ttc"pdfTemplate: "templates/logModel/pdf_export_template.ftl"waterImg: "static/images/watermark.png"

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。