1.使用Adobe Acrobat XI Pro编辑pdf模板
因为print.js不支持宋体,所以打算用后台netcore生成pdf打印,最开始生成pdf后向前台返回pdf的base64编码,但是响应时间太长,不太适合需求。
后来想着直接返回文件流给前端,前端收到pdf直接使用浏览器页面浏览,浏览器自带打印功能,省了很多事。话不多说,上代码,如有错误,请指出!!!
代码大都是网络上搜的,但多多少少有些问题,自己结合业务也做了修改
1.使用Adobe Acrobat XI Pro编辑pdf模板
Adobe Acrobat XI Pro安装包
链接:https://pan.baidu.com/s/1gLtZaGOBBx-lY_QwWTMJCg
提取码:21um
可以先使用world编辑样式
使用Adobe Acrobat XI Pro 进行编辑
以下是效果图
1 /// <summary> 2 /// PDF导出类 3 /// </summary> 4 public class PdfLeadingHelper 5 { 6 7 private static readonly ILog log = LogManager.GetLogger(typeof(PdfLeadingHelper)); 8 /// <summary> 9 /// 根据路径获取模板 10 /// </summary> 11 /// <param name="pdfTemplate"></param> 12 /// <returns></returns> 13 public static Dictionary<string, string> ReadForm(string pdfTemplate) 14 { 15 //if (Directory.Exists(PathHelper.OutPutPDFPath)) 16 //{ 17 // DelectDir(PathHelper.OutPutPDFPath); 18 //} 19 20 DeleteAllPdf(PathHelper.OutPutPDFPath); 21 22 Dictionary<string, string> dic = new Dictionary<string, string>(); 23 24 PdfReader pdfReader = null; 25 try 26 { 27 pdfReader = new PdfReader(pdfTemplate); 28 AcroFields pdfFormFields = pdfReader.AcroFields; 29 foreach (DictionaryEntry de in pdfFormFields.Fields) 30 { 31 dic.Add(de.Key.ToString(), ""); 32 } 33 } 34 catch (Exception ex) 35 { 36 dic = null; 37 //记录日志 注释 38 // LogHelper.Logger(LogLevel.Error, "pdf导出类发生异常:根据路径获取模板时异常" + ex.ToString(), ex); 39 log.Error($"{ex.Message}"); 40 } 41 finally 42 { 43 if (pdfReader != null) 44 { 45 pdfReader.Close(); 46 } 47 } 48 return dic; 49 } 50 51 public static void DelectDir(string srcPath) 52 { 53 try 54 { 55 DirectoryInfo dir = new DirectoryInfo(srcPath); 56 FileSystemInfo[] fileinfo = dir.GetFileSystemInfos(); //返回目录中所有文件和子目录 57 foreach (FileSystemInfo i in fileinfo) 58 { 59 if (i is DirectoryInfo) //判断是否文件夹 60 { 61 DirectoryInfo subdir = new DirectoryInfo(i.FullName); 62 subdir.Delete(true); //删除子目录和文件 63 } 64 else 65 { 66 File.Delete(i.FullName); //删除指定文件 67 } 68 } 69 } 70 catch (Exception ex) 71 { 72 log.Error($"{ex.Message}"); 73 } 74 } 75 76 77 /// 78 /// 向pdf模版填充内容,并生成新的文件 79 /// 80 /// 模版路径 81 /// 生成文件保存路径 82 /// 标签字典(即模版中需要填充的控件列表) 83 public static bool FillForm(string pdfTemplate, string newFile, Dictionary<string, string> dic) 84 { 85 bool rsBool = true; 86 PdfReader pdfReader = null; 87 PdfStamper pdfStamper = null; 88 FileStream fileStream = null; 89 try 90 { 91 fileStream = new FileStream(newFile, FileMode.Create); 92 pdfReader = new PdfReader(pdfTemplate); 93 pdfStamper = new PdfStamper(pdfReader, fileStream); 94 AcroFields pdfFormFields = pdfStamper.AcroFields; 95 //设置支持中文字体 96 BaseFont baseFont = BaseFont.CreateFont("C:\\WINDOWS\\FONTS\\STSONG.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); 97 pdfFormFields.AddSubstitutionFont(baseFont); 98 99 foreach (var de in dic) 100 { 101 pdfFormFields.SetField(de.Key, de.Value + ""); 102 } 103 pdfStamper.FormFlattening = true; 104 } 105 catch (Exception ex) 106 { 107 //记录日志 注释 108 // LogHelper.Logger(LogLevel.Error, "pdf导出类发生异常:向pdf模版填充内容,并生成新的文件时异常"+ex.ToString(), ex); 109 log.Error($"{ex.Message}"); 110 rsBool = false; 111 } 112 finally 113 { 114 if (pdfStamper != null) 115 { 116 pdfStamper.Close(); 117 } 118 if (pdfReader != null) 119 { 120 pdfReader.Close(); 121 } 122 if (fileStream!=null) 123 { 124 fileStream.Close(); 125 fileStream.Dispose(); 126 } 127 } 128 return rsBool; 129 } 130 131 132 133 /// <summary> 134 /// 根据模板导出PDF 135 /// </summary> 136 /// <param name="PDFTemplatePath">PDF模板</param> 137 /// <param name="keyValuePairs">文本的键值对</param> 138 /// <param name="ImageKeyValue">图片键值对</param> 139 /// <param name="fontPath">文本</param> 140 /// <returns></returns> 141 public static byte[] PDFTemplate(string PDFTemplatePath, Dictionary<string, string> keyValuePairs, string fontPath = @"C:\Windows\Fonts\simfang.ttf") 142 { 143 //Image image = Image.GetInstance(@"C:\Users\Administrator\Desktop\二维码图片_8月5日09时58分55秒.png"); 144 //return null; 145 //获取中文字体,第三个参数表示为是否潜入字体,但只要是编码字体就都会嵌入。 146 BaseFont baseFont = BaseFont.CreateFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); 147 //读取模板文件 148 PdfReader reader = new PdfReader(PDFTemplatePath); 149 150 //创建文件流用来保存填充模板后的文件 151 MemoryStream stream = new MemoryStream(); 152 153 PdfStamper stamp = new PdfStamper(reader, stream); 154 //设置表单字体,在高版本有用,高版本加入这句话就不会插入字体,低版本无用 155 stamp.AcroFields.AddSubstitutionFont(baseFont); 156 157 AcroFields form = stamp.AcroFields; 158 159 //表单文本框是否锁定 160 stamp.FormFlattening = true; 161 162 //填充表单,para为表单的一个(属性-值)字典 163 foreach (KeyValuePair<string, string> parameter in keyValuePairs) 164 { 165 //要输入中文就要设置域的字体; 166 form.SetFieldProperty(parameter.Key, "textfont", baseFont, null); 167 //为需要赋值的域设置值; 168 form.SetField(parameter.Key, parameter.Value); 169 } 170 171 //按顺序关闭io流 172 173 stamp.Close(); 174 reader.Close(); 175 176 return stream.ToArray(); 177 } 178 179 180 /// <summary> 181 /// 读取合并的pdf文件名称 182 /// </summary> 183 /// <param name="Directorypath">目录</param> 184 /// <param name="outpath">导出的路径</param> 185 public static bool MergePDF(string Directorypath, string outpath) 186 { 187 try 188 { 189 List<string> filelist2 = new List<string>(); 190 System.IO.DirectoryInfo di2 = new System.IO.DirectoryInfo(Directorypath); 191 FileInfo[] ff2 = di2.GetFiles("*.pdf"); 192 BubbleSort(ff2); 193 foreach (FileInfo temp in ff2) 194 { 195 filelist2.Add(Directorypath + temp.Name); 196 } 197 mergePDFFiles(filelist2, outpath); 198 199 //DeleteAllPdf(Directorypath); 200 return true; 201 } 202 catch (Exception ex) 203 { 204 log.Error($"{ex.Message}"); 205 return false; 206 } 207 208 } 209 210 211 /// <summary> 212 /// 冒泡排序 213 /// </summary> 214 /// <param name="arr">文件名数组</param> 215 public static void BubbleSort(FileInfo[] arr) 216 { 217 for (int i = 0; i < arr.Length; i++) 218 { 219 for (int j = i; j < arr.Length; j++) 220 { 221 if (arr[i].LastWriteTime > arr[j].LastWriteTime)//按创建时间(升序) 222 { 223 FileInfo temp = arr[i]; 224 arr[i] = arr[j]; 225 arr[j] = temp; 226 } 227 } 228 } 229 } 230 231 /// <summary> 232 /// 合成pdf文件 233 /// </summary> 234 /// <param name="fileList">文件名list</param> 235 /// <param name="outMergeFile">输出路径</param> 236 public static void mergePDFFiles(List<string> fileList, string outMergeFile) 237 { 238 PdfReader reader; 239 // Rectangle rec = new Rectangle(1660, 1000); 240 PdfWriter writer; 241 Document document = new Document(); 242 FileStream fileStream = new FileStream(outMergeFile, FileMode.Create); 243 writer = PdfWriter.GetInstance(document, fileStream); 244 document.Open(); 245 PdfContentByte cb = writer.DirectContent; 246 PdfImportedPage newPage; 247 for (int i = 0; i < fileList.Count; i++) 248 { 249 reader = new PdfReader(fileList[i]); 250 int iPageNum = reader.NumberOfPages; 251 for (int j = 1; j <= iPageNum; j++) 252 { 253 document.NewPage(); 254 newPage = writer.GetImportedPage(reader, j); 255 cb.AddTemplate(newPage, 0, 0); 256 } 257 } 258 document.Close(); 259 fileStream.Close(); 260 fileStream.Dispose(); 261 } 262 263 264 265 /// <summary> 266 /// 删除一个文件里所有的文件 267 /// </summary> 268 /// <param name="Directorypath">文件夹路径</param> 269 public static void DeleteAllPdf(string Directorypath) 270 { 271 System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(Directorypath); 272 if (di.Exists) 273 { 274 FileInfo[] ff = di.GetFiles("*.pdf"); 275 foreach (FileInfo temp in ff) 276 { 277 File.Delete(Directorypath + "\\" + temp.Name); 278 } 279 } 280 } 281 }
按照模板生成dictionary,然后数据库查询后赋值,如果数据过多,需要生成多个pdf,然后合并
最后一次数据循环,需要清空dictionary的value值,因为可能没有覆盖,显示的还是之前的值
1 public string CreateVeteranDirectory(int dType, List<TBase_VeteranDirectory> veteranDirectors) 2 { 3 List<TBase_VeteranDirectory> veteranDirectoryList = new List<TBase_VeteranDirectory>(); 4 Dictionary<string, string> dicData = PdfLeadingHelper.ReadForm(PathHelper.VeteranDirectoryPDF); 5 dicData["DocumentType"] = dType == 0 ? "一" : dType == 1 ? "二" : "三"; 6 int cnt = veteranDirectors.Count / 19 + 1; 7 for (int j = 0; j < cnt; j++) 8 { 9 if (j + 1 == cnt) 10 { 11 veteranDirectoryList = veteranDirectors.Skip(j * 19).Take(veteranDirectors.Count).ToList(); 12 string[] keys = dicData.Keys.ToArray(); 13 for (int i = 0; i < keys.Length; i++) 14 { 15 dicData[keys[i]] = ""; 16 } 17 dicData["DocumentType"] = dType == 0 ? "一" : dType == 1 ? "二" : "三"; 18 } 19 else 20 { 21 veteranDirectoryList = veteranDirectors.Skip(j * 19).Take(19 * (j + 1)).ToList(); 22 } 23 for (int i = 1; i <= veteranDirectoryList.Count; i++) 24 { 25 dicData["MaterialName_" + i] = veteranDirectoryList[i - 1].MaterialName; 26 dicData["MaterialYear_" + i] = veteranDirectoryList[i - 1].MaterialYear.ToString(); 27 dicData["MaterialMonth_" + i] = veteranDirectoryList[i - 1].MaterialMonth.ToString(); 28 dicData["MaterialDay_" + i] = veteranDirectoryList[i - 1].MaterialDay.ToString(); 29 dicData["Page_" + i] = veteranDirectoryList[i - 1].Page.ToString(); 30 dicData["Collator_" + i] = veteranDirectoryList[i - 1].Collator.ToString(); 31 } 32 PdfLeadingHelper.FillForm(PathHelper.VeteranDirectoryPDF, $"{ PathHelper.OutPutPDFPath}VeteranDirectory{j}.pdf", dicData); 33 } 34 bool res = PdfLeadingHelper.MergePDF(PathHelper.OutPutPDFPath, PathHelper.VeteranDirectoryOutPutPDF); 35 return res ? PathHelper.VeteranDirectoryOutPutPDF : ""; }
返回File就可以了
1 /// <summary> 2 /// pdf下载 3 /// </summary> 4 /// <returns></returns> 5 [HttpGet] 6 public async Task<IActionResult> DownloadDocumentPDFByType(int vId, int dType) 7 { 8 Expression<Func<TBase_VeteranDirectory, bool>> whereExpression = s => s.IsDelected == 0 && s.VId == vId && s.DType == dType; 9 var veteranDirectoryList = await _tVeteranDiretoryService.Query(whereExpression); 10 string filepath = _tVeteranDiretoryService.CreateVeteranDirectory(dType, veteranDirectoryList); 11 var provider = new FileExtensionContentTypeProvider(); 12 FileInfo fileInfo = new FileInfo(filepath); 13 var ext = fileInfo.Extension; 14 new FileExtensionContentTypeProvider().Mappings.TryGetValue(ext, out var contenttype); 15 return File(System.IO.File.ReadAllBytes(filepath), contenttype ?? "application/octet-stream", fileInfo.Name); 16 } 17 18 19 /// <summary> 20 /// 返回pdf路径 21 /// </summary> 22 /// <returns></returns> 23 [HttpGet] 24 public async Task<MessageModel<string>> GetDocumentPDFPathByType(int vId, int dType) 25 { 26 Expression<Func<TBase_VeteranDirectory, bool>> whereExpression = s => s.IsDelected == 0 && s.VId == vId && s.DType == dType; 27 var veteranDirectoryList = await _tVeteranDiretoryService.Query(whereExpression); 28 string filepath = _tVeteranDiretoryService.CreateVeteranDirectory(dType, veteranDirectoryList); 29 var serverPathList = await _tImgServerPathService.Query(); 30 string serverPath = serverPathList.FirstOrDefault().ServerPath + "\\pdf\\VeteranDirectory.pdf"; 31 string browserPath = serverPathList.FirstOrDefault().BrowserPath + "/pdf/VeteranDirectory.pdf"; 32 System.IO.File.Copy(filepath, serverPath,true); 33 return new MessageModel<string>() 34 { 35 msg = "获取成功", 36 success = true, 37 response = browserPath 38 }; 39 }
<el-dialog title="预览" :visible.sync="pdfVisible" :close-on-click-modal="false" :append-to-body="true" width="80%" top="3vh" :before-close="handleClose" :close-on-press-escape="true"> <span> <iframe :src="pdfUrl" frameborder="0" width="100%" height="550px"></iframe> </span> </el-dialog> data(){
pdfUrl: \'\', //预览地址
},
methods:{
// 第一部分打印
firstPrint: function() {
getDocumentPDFPathByType({
vId: this.id,
dType: 0,
}).then(res=>{
if (res.data.status == 200) {
// console.log(res);
this.pdfVisible = true;
this.pdfUrl = res.data.response; //请求完接口赋值pdfUrl
}
})
},
}