iText7 For .Net 系列文章(二)-文本布局、页眉、页脚、水印
按照iText为.Net提供的入门教程 的安排,第二章应该是讲关于PDF的“低级内容”(原文中称为low-level content,可以理解为更底层内容)。教程中给出了画一个坐标轴、带网格线的坐标轴以及使用画布利用文本“拉扯”做出类似“星球大战”开场标题效果。参考教程个人写了一下,个人项目中实用性不大,这里就不出教程了。直接跳到第三章,第三章讲到利用基本构建块的高级方法与更底层的方法结合起来,做一个类似报纸排版效果的PDF文件、制作一个表格,重要列标注不同颜色、设置背景色、添加页眉、页脚、水印。
实现报纸排版时遇到一个问题就是本人的文本内容是中文文本,直接使用教程中的代码发现仅文本内容中的英文内容显示在PDF中,调试发现内容从.txt文本文件中读取出来了,但是写入PDF文件就是没有显示,查阅资料发现是由于字体的原因,iText内置的字体都是英文字体,是不支持中文的。于是查阅资料实现注册中文字体功能,其中有个坑,需要注意,后续代码中会注明。
private void newYorkTimesExampleToolStripMenuItem_Click(object sender, EventArgs e) { var fullFileName = GetSavingFileNameContainDirectory(); if (string.IsNullOrWhiteSpace(fullFileName)) return; using (var fos = new FileStream(fullFileName, FileMode.Create)) { //Initialize PDF document PdfDocument pdf = new PdfDocument(new PdfWriter(fos)); PageSize ps = PageSize.A5; // Initialize document Document document = new Document(pdf, ps); //Set column parameters float offSet = 36; //这是官方实例三列的写法,此处实现为一列来展示 //float columnWidth = (ps.GetWidth() - offSet * 2 + 10) / 3; //float columnHeight = ps.GetHeight() - offSet * 2; ////Define column areas //Rectangle[] columns = new Rectangle[] { // new Rectangle(offSet - 5, offSet, columnWidth, columnHeight), // new Rectangle(offSet + columnWidth, offSet, columnWidth, columnHeight), // new Rectangle(offSet + columnWidth * 2 + 5, offSet, columnWidth, columnHeight) // }; float columnWidth = ps.GetWidth() - offSet * 2 + 10; float columnHeight = ps.GetHeight() - offSet * 2; Rectangle[] columns = new Rectangle[] { new Rectangle(offSet - 5, offSet, columnWidth, columnHeight) }; document.SetRenderer(new ColumnDocumentRenderer(document, columns)); // adding content string INST_IMG = @"此处添加图片完整路径,也可修改为选择图片"; Image inst = new Image(ImageDataFactory.Create(INST_IMG)).SetWidth(columnWidth); string INST_TXT = @"此处添加文本内容.txt文件完整路径,其他格式也是允许的"; String articleInstagram = File.ReadAllText(System.IO.Path.Combine(INST_TXT), Encoding.UTF8); NewYorkTimes.AddArticle(document, "Instagram May Change Your Feed, Personalizing It With an Algorithm" , "By MIKE ISAAC MARCH 15, 2016", inst, articleInstagram); document.Close(); } } public class NewYorkTimes { public static void AddArticle(Document doc, String title, String author, iText.Layout.Element.Image img, String text) { var timesNewRomanBold = PdfFontFactory.CreateFont(StandardFonts.TIMES_BOLD); var timesNewRoman = PdfFontFactory.CreateFont(StandardFonts.TIMES_ROMAN); //PdfFontFactory.RegisterSystemDirectories(); //获取已注册的字体名称 //List<string> registeredFonts = PdfFontFactory.GetRegisteredFonts().ToList(); //词汇必须在路径后面加上,0 不然PdfFontFactory.CreateRegisteredFont会返回null //此路径为windows操作系统自带的微软雅黑-标准字体 FontProgramFactory.RegisterFont(@"C:\Windows\Fonts\msyh.ttc,0", "yaHei_font"); PdfFont myBoldFont = PdfFontFactory.CreateRegisteredFont("yaHei_font", PdfEncodings.IDENTITY_H, EmbeddingStrategy.FORCE_EMBEDDED); var grayColor = new DeviceCmyk(0, 0, 0, 0.875f); Paragraph p1 = new Paragraph(title).SetFont(timesNewRomanBold).SetFontSize(14); doc.Add(p1); doc.Add(img); Paragraph p2 = new Paragraph().SetFont(timesNewRoman).SetFontSize(7).SetFontColor(grayColor).Add(author); doc.Add(p2); Paragraph p3 = new Paragraph().SetFont(myBoldFont).SetFontSize(10).Add(text); doc.Add(p3); } }
大致效果如下图:
添加页眉、页脚、背景色、水印,参考教程大部分内容都没问题,就是教程中添加水印功能块存在较大问题,程序会转换异常错误。又是一顿查阅资料,发现使用Canvas.SetProperty(int ,object)方法设置画笔属性时必须根据不同属性传对应的对象类型,不能像教程一样,字体大小直接传一个60的数值。自己添加了一种写起来更加简单的方式,供选择。
private void waterMarkExampleToolStripMenuItem_Click(object sender, EventArgs e) { var fullFileName = GetSavingFileNameContainDirectory(); if (string.IsNullOrWhiteSpace(fullFileName)) return; using (var fos = new FileStream(fullFileName, FileMode.Create)) { //Initialize PDF document PdfDocument pdf = new PdfDocument(new PdfWriter(fos)); pdf.AddEventHandler(PdfDocumentEvent.END_PAGE, new MyEventHandler(this)); // Initialize document Document document = new Document(pdf); Paragraph p = new Paragraph("List of reported UFO sightings in 20th century") .SetTextAlignment(TextAlignment.CENTER) .SetFont(iText.Kernel.Font.PdfFontFactory.CreateFont(StandardFonts.HELVETICA_BOLD)) .SetFontSize(14); document.Add(p); Table table = new Table(new float[] { 3, 5, 7, 4 }); table.SetWidth(UnitValue.CreatePercentValue(100)); StreamReader sr = File.OpenText(GetOpeningFileNameContainDirectory()); String line = sr.ReadLine(); Process(table, line, iText.Kernel.Font.PdfFontFactory.CreateFont(StandardFonts.HELVETICA_BOLD), true); while ((line = sr.ReadLine()) != null) { Process(table, line, iText.Kernel.Font.PdfFontFactory.CreateFont(StandardFonts.HELVETICA), false); } sr.Close(); document.Add(table); document.Close(); } } public void Process(Table table, String line, PdfFont font, bool isHeader) { StringTokenizer tokenizer = new StringTokenizer(line, ";"); int columnNumber = 0; var greenColor = new DeviceCmyk(1, 0, 1, 0.176f); var blueColor = new DeviceCmyk(1, 0.156f, 0, 0.118f); var yellowColor = new DeviceRgb(System.Drawing.Color.Yellow.R, System.Drawing.Color.Yellow.G, System.Drawing.Color.Yellow.B); var redColor = new DeviceRgb(System.Drawing.Color.Red.R, System.Drawing.Color.Red.G, System.Drawing.Color.Red.B); while (tokenizer.HasMoreTokens()) { if (isHeader) { Cell cell = new Cell().Add(new Paragraph(tokenizer.NextToken())); cell.SetNextRenderer(new RoundedCornersCellRenderer(cell)); cell.SetPadding(5).SetBorder(null); table.AddHeaderCell(cell); } else { columnNumber++; Cell cell = new Cell().Add(new Paragraph(tokenizer.NextToken())); cell.SetFont(font).SetBorder(new iText.Layout.Borders.SolidBorder(new DeviceRgb(System.Drawing.Color.Black.R, System.Drawing.Color.Black.G, System.Drawing.Color.Black.B), 0.5f)); switch (columnNumber) { case 4: { cell.SetBackgroundColor(greenColor); break; } case 5: { cell.SetBackgroundColor(yellowColor); break; } case 6: { cell.SetBackgroundColor(redColor); break; } default: { cell.SetBackgroundColor(blueColor); break; } } table.AddCell(cell); } } } public class RoundedCornersCellRenderer : CellRenderer { public RoundedCornersCellRenderer(Cell modelElement) : base(modelElement) { } public override void DrawBorder(DrawContext drawContext) { Rectangle rectangle = this.GetOccupiedAreaBBox(); float llx = rectangle.GetX() + 1; float lly = rectangle.GetY() + 1; float urx = rectangle.GetX() + this.GetOccupiedAreaBBox().GetWidth() - 1; float ury = rectangle.GetY() + this.GetOccupiedAreaBBox().GetHeight() - 1; PdfCanvas canvas = drawContext.GetCanvas(); float r = 4; float b = 0.4477f; canvas.MoveTo(llx, lly).LineTo(urx, lly).LineTo(urx, ury - r).CurveTo(urx, ury - r * b, urx - r * b, ury, urx - r, ury).LineTo(llx + r, ury).CurveTo(llx + r * b, ury, llx, ury - r * b, llx, ury - r).LineTo(llx , lly).Stroke(); base.DrawBorder(drawContext); } } public class MyEventHandler : IEventHandler { public MyEventHandler(Form enclosing) { _enclosing = enclosing; } public virtual void HandleEvent(Event @event) { PdfDocumentEvent docEvent = (PdfDocumentEvent)@event; PdfDocument pdfDoc = docEvent.GetDocument(); PdfPage page = docEvent.GetPage(); int pageNumber = pdfDoc.GetPageNumber(page); Rectangle pageSize = page.GetPageSize(); PdfCanvas pdfCanvas = new PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), pdfDoc);//此方式,水印在内容下层,会被文本内容遮挡 //Set background Color limeColor = new DeviceCmyk(0.208f, 0, 0.584f, 0); Color blueColor = new DeviceCmyk(0.445f, 0.0546f, 0, 0.0667f); var helveticaFont = iText.Kernel.Font.PdfFontFactory.CreateFont(StandardFonts.HELVETICA); var helveticaBoldFont = iText.Kernel.Font.PdfFontFactory.CreateFont(StandardFonts.HELVETICA_BOLD); pdfCanvas.SaveState() .SetFillColor(pageNumber % 2 == 1 ? limeColor : blueColor) .Rectangle(pageSize.GetLeft(), pageSize.GetBottom(), pageSize.GetWidth(), pageSize.GetHeight()) .Fill() .RestoreState(); //Add header and footer pdfCanvas.BeginText() .SetFontAndSize(helveticaFont, 9) .MoveText(pageSize.GetWidth() / 2 - 60, pageSize.GetTop() - 20) .ShowText("THE TRUTH IS OUT THERE") .MoveText(60, -pageSize.GetTop() + 30) .ShowText(pageNumber.ToString()) .EndText(); PdfCanvas overCanvas = new PdfCanvas(page);//水印在内容上面,不会被遮挡 //Add watermark Color whiteColor = new DeviceRgb(System.Drawing.Color.White.R, System.Drawing.Color.White.G, System.Drawing.Color.White.B); Color blackColor = new DeviceRgb(System.Drawing.Color.Black.R, System.Drawing.Color.Black.G, System.Drawing.Color.Black.B); //方式1 //iText.Layout.Canvas canvas = new iText.Layout.Canvas(overCanvas, page.GetPageSize()); //UnitValue fontSize = new UnitValue(UnitValue.POINT, 60); //TransparentColor fontColor = new TransparentColor(whiteColor, 0.8f); ////不能使用注释掉的方式(注释内容是入门教程中的写法)去设置文本的样式,不然在ShowTextAligned中会报转换异常 //canvas.SetProperty(Property.FONT_COLOR, fontColor);//canvas.SetProperty(Property.FONT_COLOR, whiteColor); //canvas.SetProperty(Property.FONT_SIZE, fontSize);//canvas.SetProperty(Property.FONT_SIZE, 60); //canvas.SetProperty(Property.FONT, helveticaBoldFont); //canvas.ShowTextAligned(new Paragraph("CONFIDENTIAL"), 298, 421, pdfDoc.GetPageNumber(page), TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45); //canvas.Close(); //方式2 推荐方式2,书写更简单 //overCanvas.SaveState(); //Paragraph paragraph = new Paragraph("CONFIDENTIAL") // .SetFont(helveticaBoldFont) // .SetFontSize(60) // .SetFontColor(whiteColor) // .SetOpacity(0.8f); ////直接给Paragraph设置透明度比下面这种方式更简单 ////PdfExtGState gs1 = new PdfExtGState(); ////gs1.SetFillOpacity(0.5f);//设置透明度 ////overCanvas.SetExtGState(gs1); //Canvas canvasWatermark1 = new Canvas(overCanvas, pdfDoc.GetDefaultPageSize()) // .ShowTextAligned(paragraph, 298, 421, pdfDoc.GetPageNumber(page), TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45); //canvasWatermark1.Close(); //overCanvas.RestoreState(); pdfCanvas.Release(); } private readonly Form _enclosing; }
里面添加了单元格样式、PDF处理事件两个类,用于实现自定义的单元格样式和页眉、页脚、背景色、水印自定义事件(每次发生 PdfDocumentEvent.END_PAGE 类型的事件时都会触发此方法。即:每次 iText 完成向页面添加内容时,要么是因为创建了新页面,要么是因为已到达并完成最后一页。查看了一下END_PAGE的解释就是 在页面刷新到文档之前调度的事件类型)。效果如下图,背景色按照奇数柠檬黄、偶数绿色设置,但是其中有一个问题,除了第一页第一行有圆角设置外后面页的第一行标题没有格式,这个问题抽时间解决后再来更新一下文章,如果知道的小伙伴在评论里留言指出我会非常感谢。
如果该文章对你有所帮助,还望推荐一下。如果慷慨的你愿意打赏,那就再好不过了。
本文来自博客园,作者:业荒于嬉,转载请注明原文链接:https://www.cnblogs.com/FreeLoopPowter/p/15687440.html
如内容对您有所帮助,还请不吝推荐一下!