上周按需求需要在系统上实现PDF带水印文档的导出,网上搜了一下发现开源的组件还挺多的,用的比较多的是ITextsharp,Demo也相对较多,就它了!
(一)首先下载ITextSharp.dll,可在官网上下载:http://sourceforge.net/projects/itextsharp/,本人用了itextsharp-dll-core文件夹下的dll。
(二)网上有很多类似的Demo和ITextSharp各属性的详细讲解,就不做重复累赘了,可自行百度参考。
(三)使用过程:在需要的地方调用CreatePDF.Instance.GeneratePDF()即可,详细代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 //要用到的命名空间 2 using System.IO; 3 using iTextSharp.text; 4 using iTextSharp.text.pdf; 5 using System.Text; 6 using System.Data; 7 public class CreatePDF 8 { 9 private static CreatePDF instance; 10 public static CreatePDF GetInstance() 11 { 12 if (instance == null) 13 { 14 instance = new CreatePDF(); 15 } 16 return instance; 17 } 18 19 private static Document doc; 20 private static BaseFont bf = BaseFont.CreateFont(@"C://Windows/Fonts/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); 21 //四种字体 22 private static Font fontBig = new Font(bf, 20, Font.BOLD); 23 private static Font fontMiddle = new Font(bf, 15, Font.BOLD); 24 private static Font fontSmall = new Font(bf, 13, Font.BOLD); 25 private static Font fontSmallNoBold = new Font(bf, 13); 26 private static float IndentationLeft = 50;//距左边距 27 //如果要传参数进来,可自定义 28 public void GeneratePDF() 29 { 30 doc = new Document(PageSize.A4); 31 try 32 { 33 //MemoryStream ms2 = new MemoryStream(); 34 string fileName = string.Format("数据_{0}.pdf", DateTime.Now.ToString("yyyyMMddHHmmss")); 35 string filePath = string.Format("{0}/Resource/{1}", AppDomain.CurrentDomain.BaseDirectory, fileName); 36 FileStream fs = new FileStream(filePath, FileMode.Create);//创建临时文件,到时生成好后删除 37 PdfWriter writer = PdfWriter.GetInstance(doc, fs); 38 writer.CloseStream = false;//把doc内容写入流中 39 doc.Open(); 40 41 //核心操作 42 CreateLine();//生成一条下横线 43 CreateEmptyRow(1);//生成一行空行 44 AddHeaderTitleContent("表10 已安装工程量明细表");//添加表头 45 CreateEmptyRow(1);//生成一行空行 46 AddPartnerContent("工程名称", "建设名称", "施工名称", "监理名称");//添加合作单位 47 AddPageNumberContent();//添加页码 48 CreateEmptyRow(1);//生成一行空行 49 50 #region 生成表格数据 51 PdfPTable table = new PdfPTable(6);//6列的table 52 //添加表格列头 53 table.SetTotalWidth(new float[] { 50, 200, 60, 60, 100, 100 }); 54 table.AddCell(GetPdfCell("序号", fontSmallNoBold, Element.ALIGN_CENTER)); 55 table.AddCell(GetPdfCell("工程量名称", fontSmallNoBold, Element.ALIGN_CENTER)); 56 table.AddCell(GetPdfCell("单位", fontSmallNoBold, Element.ALIGN_CENTER)); 57 table.AddCell(GetPdfCell("数量", fontSmallNoBold, Element.ALIGN_CENTER)); 58 table.AddCell(GetPdfCell("安装地点、路段", fontSmallNoBold, Element.ALIGN_CENTER)); 59 table.AddCell(GetPdfCell("备注", fontSmallNoBold, Element.ALIGN_CENTER)); 60 61 int emptyRow = 20;//如果table的行数小于20行,那么剩余部分显示空白行 62 #region 构造数据源 63 DataTable tableSource = new DataTable(); 64 tableSource.Columns.Add(new DataColumn("aa")); 65 tableSource.Columns.Add(new DataColumn("bb")); 66 tableSource.Columns.Add(new DataColumn("cc")); 67 tableSource.Columns.Add(new DataColumn("dd")); 68 tableSource.Columns.Add(new DataColumn("ee")); 69 for (int i = 0; i < 15; i++) 70 { 71 DataRow row = tableSource.NewRow(); 72 row["aa"] = "aa"; 73 row["bb"] = "bb"; 74 row["cc"] = "cc"; 75 row["dd"] = "dd"; 76 row["ee"] = "ee"; 77 tableSource.Rows.Add(row); 78 } 79 #endregion 80 if (tableSource.Rows.Count > 0) 81 { 82 emptyRow = emptyRow - tableSource.Rows.Count;//如果为负数,说明不需要生成空白行 83 for (int i = 0; i < tableSource.Rows.Count; i++) 84 { 85 DataRow row = tableSource.Rows[i]; 86 table.AddCell(GetPdfCell((i + 1).ToString(), fontSmallNoBold, Element.ALIGN_CENTER)); 87 table.AddCell(GetPdfCell(row["aa"].ToString(), fontSmallNoBold, Element.ALIGN_CENTER)); 88 table.AddCell(GetPdfCell(row["bb"].ToString(), fontSmallNoBold, Element.ALIGN_CENTER)); 89 table.AddCell(GetPdfCell(row["cc"].ToString(), fontSmallNoBold, Element.ALIGN_CENTER)); 90 table.AddCell(GetPdfCell(row["dd"].ToString(), fontSmallNoBold, Element.ALIGN_CENTER)); 91 table.AddCell(GetPdfCell(row["ee"].ToString(), fontSmallNoBold, Element.ALIGN_CENTER)); 92 } 93 } 94 if (emptyRow > 0)//说明数据源不足20行 95 { 96 for (int i = 0; i < emptyRow; i++) 97 { 98 table.AddCell(GetPdfCell(((20 - emptyRow) + i + 1).ToString(), fontSmallNoBold, Element.ALIGN_CENTER)); 99 table.AddCell(GetPdfCell("", fontSmallNoBold, Element.ALIGN_CENTER)); 100 table.AddCell(GetPdfCell("", fontSmallNoBold, Element.ALIGN_CENTER)); 101 table.AddCell(GetPdfCell("", fontSmallNoBold, Element.ALIGN_CENTER)); 102 table.AddCell(GetPdfCell("", fontSmallNoBold, Element.ALIGN_CENTER)); 103 table.AddCell(GetPdfCell("", fontSmallNoBold, Element.ALIGN_CENTER)); 104 } 105 } 106 doc.Add(table); 107 #endregion 108 109 #region 添加水印 110 string waterMarkName = "经度:无 纬度:无 地址:无 验收负责人:无"; 111 string waterMarkAddr = "广州市天河区"; 112 if (!string.IsNullOrEmpty(waterMarkAddr)) 113 { 114 waterMarkName = string.Format("经度:{0} 纬度:{1} 地址:{2} 验收负责人:{3}", "113.211", "23.211", waterMarkAddr, "张三"); 115 } 116 #endregion 117 118 doc.Close(); 119 MemoryStream ms = new MemoryStream(); 120 if (fs != null) 121 { 122 byte[] bytes = new byte[fs.Length];//定义一个长度为fs长度的字节数组 123 fs.Read(bytes, 0, (int)fs.Length);//把fs的内容读到字节数组中 124 ms.Write(bytes, 0, bytes.Length);//把字节内容读到流中 125 fs.Flush(); 126 fs.Close(); 127 } 128 MemoryStream waterMS = SetWaterMark(ms, filePath, waterMarkName);//先生成水印,再删除临时文件 129 if (File.Exists(filePath))//判断临时文件是否存在,如果存在则删除 130 { 131 File.Delete(filePath); 132 GC.Collect();//回收垃圾 133 } 134 SendFile(fileName, waterMS);//把PDF文件发送回浏览器 135 } 136 catch (DocumentException ex) 137 { 138 throw new Exception(ex.Message); 139 } 140 } 141 #region 生成一条横线 142 private static void CreateLine() 143 { 144 PdfPTable table = new PdfPTable(1);//一个单元格的 145 table.TotalWidth = 500; 146 PdfPCell cell = new PdfPCell(); 147 cell.BorderWidthBottom = 0.5f; 148 table.AddCell(cell); 149 doc.Add(table); 150 } 151 #endregion 152 153 #region 生成N行空白行 154 private static void CreateEmptyRow(int emptyRowNum) 155 { 156 for (int i = 0; i < emptyRowNum; i++) 157 { 158 doc.Add(new Paragraph(" ")); 159 } 160 } 161 #endregion 162 163 #region 生成标题 164 private static void AddHeaderTitleContent(string content) 165 { 166 Paragraph p = new Paragraph(content, fontMiddle); 167 p.IndentationLeft = IndentationLeft;//距离左边距 168 doc.Add(p); 169 } 170 #endregion 171 172 #region 生成合作单位 173 private static void AddPartnerContent(string stationName, string constructUnit, string buildUnit, string supervisionUnit) 174 { 175 fontMiddle.SetStyle(Font.UNDERLINE);//文字下划线 176 IndentationLeft = IndentationLeft + 10; 177 178 Paragraph content = new Paragraph(); 179 content.IndentationLeft = IndentationLeft; 180 Chunk chunkName = new Chunk("单项或单位工程名称:", fontSmallNoBold); 181 Chunk chunkText = new Chunk(GetEmptyString(25, stationName), fontMiddle); 182 content.Add(0, chunkName); 183 content.Add(1, chunkText); 184 content.Alignment = 3; 185 doc.Add(content); 186 187 content = new Paragraph(); 188 content.IndentationLeft = IndentationLeft; 189 chunkName = new Chunk("建设单位名称:", fontSmallNoBold); 190 chunkText = new Chunk(GetEmptyString(30, constructUnit), fontMiddle); 191 content.Add(0, chunkName); 192 content.Add(1, chunkText); 193 content.Alignment = 3; 194 doc.Add(content); 195 196 content = new Paragraph(); 197 content.IndentationLeft = IndentationLeft; 198 chunkName = new Chunk("施工单位名称:", fontSmallNoBold); 199 chunkText = new Chunk(GetEmptyString(30, buildUnit), fontMiddle); 200 content.Add(0, chunkName); 201 content.Add(1, chunkText); 202 content.Alignment = 3; 203 doc.Add(content); 204 205 content = new Paragraph(); 206 content.IndentationLeft = IndentationLeft; 207 chunkName = new Chunk("监理单位名称:", fontSmallNoBold); 208 chunkText = new Chunk(GetEmptyString(30, supervisionUnit), fontMiddle); 209 content.Add(0, chunkName); 210 content.Add(1, chunkText); 211 content.Alignment = 3; 212 doc.Add(content); 213 } 214 //居中显示内容 215 private static string GetEmptyString(int maxlength, string text) 216 { 217 int padding = (maxlength - text.Length * 2) / 2; 218 string empty = string.Empty; 219 for (int i = 0; i < padding; i++) 220 { 221 empty += " "; 222 } 223 return string.Format("{0}{1}{0}", empty, text); 224 } 225 #endregion 226 227 #region 生成页码 228 private static void AddPageNumberContent() 229 { 230 var content = new Paragraph("共 页 第 页", fontSmall); 231 content.IndentationRight = IndentationLeft + 20; 232 content.Alignment = 2; //居左 233 doc.Add(content); 234 } 235 #endregion 236 237 #region 生成单元格 238 private static PdfPCell GetPdfCell(string content, Font font, int horizontalAlignment) 239 { 240 PdfPCell cell = new PdfPCell(new Paragraph(content, font)); 241 cell.HorizontalAlignment = horizontalAlignment;//水平位置 242 cell.VerticalAlignment = Element.ALIGN_CENTER;//垂直居中 243 cell.MinimumHeight = 20;//单元格的最小高度 244 return cell; 245 } 246 #endregion 247 248 #region 生成水印 249 private static MemoryStream SetWaterMark(MemoryStream ms, string filePath, string waterMarkName, string waterMarkAddr = null) 250 { 251 MemoryStream msWater = new MemoryStream(); 252 PdfReader pdfReader = null; 253 PdfStamper pdfStamper = null; 254 try 255 { 256 pdfReader = new PdfReader(filePath); 257 pdfStamper = new PdfStamper(pdfReader, msWater); 258 259 int total = pdfReader.NumberOfPages + 1;//获取PDF的总页数 260 iTextSharp.text.Rectangle psize = pdfReader.GetPageSize(1);//获取第一页 261 float width = psize.Width;//PDF页面的宽度,用于计算水印倾斜 262 float height = psize.Height; 263 PdfContentByte waterContent; 264 BaseFont basefont = BaseFont.CreateFont(@"C:\WINDOWS\Fonts\SIMFANG.TTF", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); 265 PdfGState gs = new PdfGState(); 266 for (int i = 1; i < total; i++) 267 { 268 waterContent = pdfStamper.GetOverContent(i);//在内容上方加水印 269 //透明度 270 waterContent.SetGState(gs); 271 //开始写入文本 272 waterContent.BeginText(); 273 waterContent.SetColorFill(BaseColor.RED); 274 waterContent.SetFontAndSize(basefont, 18); 275 waterContent.SetTextMatrix(0, 0); 276 if (waterMarkAddr == null || waterMarkAddr == "") 277 { 278 waterContent.ShowTextAligned(Element.ALIGN_CENTER, waterMarkName, width / 2, height / 2, 55); 279 } 280 else 281 { 282 waterContent.ShowTextAligned(Element.ALIGN_CENTER, waterMarkName, width / 2, height / 2 + 100, 55); 283 waterContent.ShowTextAligned(Element.ALIGN_CENTER, waterMarkAddr, width / 2, height / 2 - 100, 55); 284 } 285 waterContent.EndText(); 286 } 287 } 288 catch (Exception) 289 { 290 return ms; 291 } 292 finally 293 { 294 if (pdfStamper != null) 295 pdfStamper.Close(); 296 297 if (pdfReader != null) 298 pdfReader.Close(); 299 } 300 return msWater; 301 } 302 #endregion 303 304 //-----------------------发送PDF文件回浏览器端---------------------- 305 public static void SendFile(string fileName, MemoryStream ms, Encoding encoding = null) 306 { 307 fileName = (fileName + "").Replace(" ", ""); 308 encoding = encoding ?? Encoding.UTF8; 309 if (ms != null && !string.IsNullOrEmpty(fileName)) 310 { 311 System.Web.HttpResponse response = System.Web.HttpContext.Current.Response; 312 response.Clear(); 313 response.Charset = encoding.BodyName;// "utf-8"; 314 if (!HttpContext.Current.Request.UserAgent.Contains("Firefox") && !HttpContext.Current.Request.UserAgent.Contains("Chrome")) 315 { 316 fileName = HttpUtility.UrlEncode(fileName, encoding); 317 } 318 response.AddHeader("Content-Disposition", "attachment;filename=" + fileName); 319 //为了解决打开,导出NPOI生成的xlsx文件时,提示发现不可读取内容。 320 if (!(fileName + "").ToLower().EndsWith(".xlsx")) 321 { 322 response.AddHeader("Content-Type", "application/octet-stream"); 323 response.BinaryWrite(ms.GetBuffer()); 324 } 325 else 326 { 327 response.BinaryWrite(ms.ToArray()); 328 } 329 ms.Close(); 330 ms = null; 331 response.Flush(); 332 response.End(); 333 } 334 } 335 }
(四)总结:
从代码上,不难看出,普遍用到的标签大多为Document、Paragraph、Table、Cell、Chunck、Font、Element,通过这些标签大概能组合出自己想要的效果,有点类似Html思想。
a.通过Paragraph、chunck即可显示行元素,若要设置间距或居中等效果,需要熟悉对属性的使用,如设置居中需要Element.ALIGN_CENTER
b.table操作cell,cell内要添加Paragraph来显示内容,如果需要跨行跨列效果,则可通过cell.Colspan或cell.Rowspan来设置,
c.实现上不难,如果要达到自己的效果,得多熟悉元素属性的使用和多思考多实践