1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 基于itext的html转pdf的代码实现 亲测可用

基于itext的html转pdf的代码实现 亲测可用

时间:2019-02-12 13:43:54

相关推荐

基于itext的html转pdf的代码实现 亲测可用

项目背景

近期在做一个web项目的打印功能,本来都已经做好了,但是客户的使用场景是钉钉工作台内嵌的浏览器,由于钉钉目前不支持标准浏览器的打印功能,所以打印在钉钉内无法使用,故而寻找其他的解决办法:通过将页面导出为pdf,然后再打印。网上也已经有itext转pdf的例子,但是大多都是copy & paste,有些不能直接使用,并且转换效果不好,更直接的就是中文不能直接换行,导致表格直接超出一个A4纸的大小。经过大量的百度,我整理出了一个可行的方法,话不多说直接上代码,代码可直接copy使用。第一次写博客,不喜勿喷!!

效果图

开发环境

后端基于jdk 1.8 ,springboot2.0 前端基于ng-alain

资源

simsun.ttc

<dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.4.3</version></dependency><dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf-itext5</artifactId><version>9.1.18</version></dependency><resources><resource><directory>src/main/resources</directory><filtering>true</filtering><excludes><exclude>fonts/*</exclude></excludes></resource><resource><directory>src/main/resources</directory><filtering>false</filtering><includes><include>fonts/*</include></includes></resource></resources>

大段代码

前端代码

ponent.html

<div #content id="content"><div class="page"><h3 style="text-align: center">采购申请单</h3><div style="height:32px"><div style="float: left;">打印时间:{{ dateTime | date:'yyyy-MM-dd HH:mm:ss'}}</div><div style="float: right;">打印人:{{ settings.user.realname }}</div></div><table class="purchaseTable"><tr><td class="header">申请单号</td><td>{{purchase.code}}</td><td class="header">需求日期</td><td colspan="2">{{purchase.reqDate}}</td><td class="header">采购类别</td><td>{{purchase.category}}</td></tr><tr><td class="header">IT相关采购</td><td>{{purchase.itPurchase == '1'?'是':'否'}}</td><td class="header" style="white-space:nowrap;word-break: keep-all;">是否定向采购</td><td colspan="2">{{purchase.directPurchase == '1'?'是':'否'}}</td><td class="header" style="white-space:nowrap;word-break: keep-all;">是否自行采购</td><td>{{purchase.selfPurchase == '1'?'是':'否'}}</td></tr><tr><td class="header">需求部门</td><td colspan="2">{{purchase.pkDepartName}}</td><td class="header" colspan="2">申请人</td><td colspan="2">{{purchase.applicant}}</td></tr><tr><td class="header">项目采购/非项目采购</td><td>{{purchase.projectPurchase == '1'?'项目采购':'非项目采购'}}</td><td class="header">项目名称/预算名称</td><td colspan="2"><ng-container *ngIf="purchase.projectPurchase == '1'; else elseName">{{purchase.pkProjectName}}</ng-container><ng-template #elseName>{{purchase.budgetName}}</ng-template></td><td class="header">项目余额/预算余额</td><td><ng-container *ngIf="purchase.projectPurchase == '1'; else elseAmount">{{purchase.predictAmount}}</ng-container><ng-template #elseAmount>{{purchase.budget}}</ng-template></td></tr><tr *ngIf="purchase.directPurchase == '1'"><td class="header">定向原因</td><td colspan="6">{{purchase.directReson}}</td></tr><tr><td class="header">采购事由</td><td colspan="6">{{purchase.purchaseReason}}</td></tr><tr><td class="header">特殊需求说明</td><td colspan="6">{{purchase.specialExplain}}</td></tr><tr><td class="header">联系人</td><td>{{purchase.contactPsn}}</td><td class="header">联系电话</td><td>{{purchase.contactTel}}</td><td class="header">地址</td><td colspan="2">{{purchase.address}}</td></tr><tr><td class="header">备注</td><td colspan="6">{{purchase.memo}}</td></tr><tr><td class="header">附件</td><td colspan="6"><ng-container *ngIf="purchase.fileList && purchase.fileList.length>0; else elseTemplate"><div *ngFor="let item of purchase.fileList"><i nz-icon nzType="file" nzTheme="outline"></i>{{item.name}}</div></ng-container><ng-template #elseTemplate>无</ng-template></td></tr><tr><td class="header">产品编码</td><td class="header">产品名称</td><td class="header">单位</td><td class="header">技术参数</td><td class="header">数量</td><td class="header">预估含税单价</td><td class="header">预估总价</td></tr><tr *ngFor="let item of materialList"><td class="text-center">{{item.materialCode}}</td><td class="text-center">{{item.materialName}}</td><td class="text-center">{{item.materialUnit}}</td><td class="text-center">{{item.materialTechParam}}</td><td class="text-center">{{item.num}}</td><td class="text-center">{{item.predictPrice}}</td><td class="text-center">{{item.totalPrice}}</td></tr><tr><td colspan="2" class="header">推荐供应商名称</td><td class="header">联系人</td><td colspan="2" class="header">联系电话</td><td colspan="2" class="header">邮箱</td></tr><tr *ngFor="let item of supplierList"><td colspan="2" class="text-center" style="height: 39px;">{{item.supplierName}}</td><td class="text-center">{{item.supplierContact}}</td><td colspan="2" class="text-center">{{item.supplierContactTel}}</td><td colspan="2" class="text-center">{{item.supplierContactEmail}}</td></tr><tr><td class="header" rowSpan="opinions.length">审批信息</td><td colspan="6"><ng-container *ngFor="let ta of opinions;let i = index"><div *ngIf="i!=opinions.length-1" style="border-bottom:1px gray solid">{{ta.personnelName}}:{{ta.opinion}}<span style="float: right;">{{ta.dealTime}}</span></div><div *ngIf="i==opinions.length-1">{{ta.personnelName}}:{{ta.opinion}}<span style="float: right;">{{ta.dealTime}}</span></div></ng-container><div *ngIf="copyPerson!=null && copyPerson.length>0" style="border-top:1px gray solid">抄送人:<span *ngFor="let cp of copyPerson;">{{cp.personnelName}}</span></div></td></tr><tr><td class="header" rowSpan="data.length">评论信息</td><td colspan="6"><ng-container *ngFor="let ts of data;let i = index"><div *ngIf="i!=data.length-1" style="border-bottom:1px gray solid">{{ts.personnelName}}:{{ts.text}}<div style="float: right;">{{ts.createTime}}</div></div><div *ngIf="i==data.length-1">{{ts.personnelName}}:{{ts.text}}<div style="float: right;">{{ts.createTime}}</div></div></ng-container></td></tr></table></div></div><div class="drawer-footer"><button type="button" (click)="exportPdf()" class="ant-btn" style="margin-right: 8px;">导出为PDF</button></div>

ponent.ts

import { Component, OnInit, ViewChild, Input, ElementRef } from '@angular/core';import { _HttpClient, ModalHelper } from '@delon/theme';import { SettingsService } from '@delon/theme';import { environment } from '@env/environment';import { DictService } from 'app/services/dict.service';import { NzMessageService, NzNotificationService, NzDrawerRef } from 'ng-zorro-antd';import { Purchase } from 'app/entity/basedata/purchase';import { PDFViewerComponent } from '@shared/common/office/pdfviewer/ponent';import { PurchaseService } from 'app/services/basedata/purchase.service';import { ProcessService } from 'app/services/process/process.service';import { ProcessComment } from 'app/entity/process/processComment';import { EssenceNg2PrintComponent } from 'essence-ng2-print';import { DownloadService } from 'app/services/download.service';@Component({selector: 'app-purchase-req-printing',templateUrl: './ponent.html',styles:[`.page {width: 21cm;min-height: 29.7cm;padding: 2cm;margin: 1cm auto;border: 1px #D3D3D3 solid;border-radius: 5px;background: white;box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);position: relative;}.header{text-align:center;}.text-center{text-align:center;}.purchaseTable{width:100%;word-wrap:break-word;border: 1px gray solid;border-spacing: 0px;border-collapse: collapse;line-height:36px;}.purchaseTable td{padding:0 3px;border: 1px gray solid;}`]})export class PurchaseReqPrintingComponent implements OnInit {purchaseCategorys: any;appendixCategorys:any;prefix = environment.SERVER_URL+"sys/common/downloadFile/";constructor(private dictService:DictService,private modalHelper:ModalHelper,private msg:NzMessageService,private purchareService : PurchaseService,private notification:NzNotificationService,private drawerRef: NzDrawerRef,private processService:ProcessService,public settings: SettingsService,private download:DownloadService) { this.printStyle = `.purchaseTable{width:100%;word-wrap:break-word;border: 1px gray solid;border-spacing: 0px;border-collapse: collapse;line-height:36px;}.purchaseTable td{border: 1px gray solid;padding:0 3px;}.purchaseTable tr{page-break-inside: avoid;}.header{text-align:center;}.text-center{text-align:center;}`}ngOnInit() {//加载数据,代码略}@ViewChild('content', {read: false}) content: ElementRef;exportPdf(){const printHtml = document.getElementById('content').innerHTML;// let preAddHtml = "<!DOCTYPE html[<!ENTITY nbsp ' '>]>";let preAddHtml = "<html>";preAddHtml += "<head>";preAddHtml += "<meta http-equiv='Content-Type' content='text/html; charset=utf-8'></meta>";preAddHtml += "<style type='text/css'>";preAddHtml += ".page{size:a4}.purchaseTable{width:100%;word-wrap:break-word;border: 1px gray solid;border-spacing: 0px;border-collapse: collapse;line-height:36px;}";preAddHtml += ".purchaseTable td{border: 1px gray solid;padding:0 3px;}";preAddHtml += "@page{size:A4 }";preAddHtml += "table{table-layout:fixed; word-break:break-strict;}";preAddHtml += ".purchaseTable tr{page-break-inside: avoid;}";preAddHtml += ".header{text-align:center;}";preAddHtml += ".text-center{text-align:center;}";preAddHtml += "</style>";preAddHtml += "</head>";preAddHtml += "<body style='font-family:SimSun;'>";let suffixAddHtml = "</body>";suffixAddHtml += "</html>";const html = preAddHtml + printHtml + suffixAddHtml;const body = {htmlStr:html,code:this.purchase.code}this.dictService.html2pdf(body).then((response)=>{this.download.requestBlob(environment.SERVER_URL+"sys/common/downloadFile/"+response.result).subscribe(result => {this.download.downFile(result,this.purchase.code+".pdf");})})}}

downloadService

import { Injectable } from '@angular/core';import { HttpClient, HttpParams } from '@angular/common/http';import { Observable } from 'rxjs';@Injectable({providedIn: 'root'})export class DownloadService {constructor(private http: HttpClient) { }//Blob请求requestBlob(url:any,data?:any):Observable<any>{return this.http.request("get",url,{body: data, observe: 'response',responseType:'blob'});}//Blob文件转换下载downFile(result,fileName,fileType?){var data=result.body;var blob = new Blob([data], {type: fileType||data.type});var objectUrl = URL.createObjectURL(blob);var a = document.createElement('a');a.setAttribute('style', 'display:none');a.setAttribute('href', objectUrl);a.setAttribute('download', fileName);a.click();URL.revokeObjectURL(objectUrl);}}

pdf工具类

import com.itextpdf.text.DocumentException;import com.itextpdf.text.pdf.BaseFont;import org.springframework.util.ResourceUtils;import org.xhtmlrenderer.pdf.ITextFontResolver;import org.xhtmlrenderer.pdf.ITextRenderer;import java.io.File;import java.io.IOException;import java.io.OutputStream;public class PDFUtil {/*** 通过html的字符串转pdf* @param out* @param html* @throws IOException* @throws DocumentException*/public static void createPdfByHtml(OutputStream out, String html) {try {ITextRenderer renderer = new ITextRenderer();renderer.setDocumentFromString(html);// 解决中文支持问题ITextFontResolver fontResolver = renderer.getFontResolver();fontResolver.addFont("/fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);renderer.layout();renderer.createPDF(out);} catch (Exception e) {e.printStackTrace();}}/*** 通过html的文件路径转pdf* @param out* @param htmlFilePath* @throws IOException* @throws DocumentException*/public static void createPdfByUrl(OutputStream out,String htmlFilePath) {try {ITextRenderer renderer = new ITextRenderer();String url = new File(htmlFilePath).toURI().toURL().toString();renderer.setDocument(url);// 解决中文支持问题ITextFontResolver fontResolver = renderer.getFontResolver();String fontPath = ResourceUtils.getURL("classpath:templates/font/simsun.ttc").getPath();fontResolver.addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);//解决图片的相对路径问题//renderer.getSharedContext().setBaseURL("http://localhost:8080");//file:/e:/renderer.layout();renderer.createPDF(out);} catch (Exception e) {e.printStackTrace();}}}

后端请求并把html字符串转换为pdf,存在服务器

/***html生成pdf*/@PostMapping(value = "/html2pdf")public Result<String> html2pdf(@RequestBody Map<String,String> mp) throws Exception{Result<String> result = new Result<>();String htmlStr = mp.get("htmlStr");String code = mp.get("code");String ctxPath = uploadpath;String bizPath = "html2pdf";File file = new File(ctxPath + File.separator + bizPath);if (!file.exists()) {file.mkdirs();// 创建文件根目录}String pdfPath = ctxPath + File.separator + bizPath + File.separator + code + ".pdf";BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(pdfPath)));PDFUtil.createPdfByHtml(bos,htmlStr);String filePath = bizPath + File.separator + code + ".pdf";result.setResult(filePath);result.setSuccess(true);return result;}

下载文件的接口

/*** 获取文件* 请求地址:http://localhost:8080/common/downloadFile/{file/0119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}** @param request* @param response*/@GetMapping(value = "/downloadFile/**")public void downloadFile(HttpServletRequest request, HttpServletResponse response) {// ISO-8859-1 ==> UTF-8 进行编码转换String filePath = extractPathFromPattern(request);// 其余处理略InputStream inputStream = null;OutputStream outputStream = null;try {filePath = filePath.replace("..", "");if (filePath.endsWith(",")) {filePath = filePath.substring(0, filePath.length() - 1);}String localPath = uploadpath;String fileurl = localPath + File.separator + filePath;String fileName = fileurl.substring(fileurl.lastIndexOf("/")+1);//浏览器设置,处理下载文件时文件名乱码String userAgent = request.getHeader("User-Agent");if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {//IE浏览器处理fileName = .URLEncoder.encode(fileName, "UTF-8");} else {// 非IE浏览器的处理:fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");}response.setContentType("application/octet-stream");response.setHeader("content-type", "application/octet-stream");response.addHeader("Content-Length", "" + new File(fileurl).length());response.setHeader("Content-Disposition", "attachment;fileName=" + fileName );// 设置文件名inputStream = new BufferedInputStream(new FileInputStream(fileurl));outputStream = response.getOutputStream();byte[] buf = new byte[1024];int len;while ((len = inputStream.read(buf)) > 0) {outputStream.write(buf, 0, len);}response.flushBuffer();} catch (IOException e) {log.info("预览图片失败" + e.getMessage());// e.printStackTrace();} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}}

参考:/p/dd94d291ed57

/c1149418436/article/details/83862455

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