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的解释就是 在页面刷新到文档之前调度的事件类型)。效果如下图,背景色按照奇数柠檬黄、偶数绿色设置,但是其中有一个问题,除了第一页第一行有圆角设置外后面页的第一行标题没有格式,这个问题抽时间解决后再来更新一下文章,如果知道的小伙伴在评论里留言指出我会非常感谢。

 如果该文章对你有所帮助,还望推荐一下。如果慷慨的你愿意打赏,那就再好不过了。

posted @ 2021-12-15 14:41  业荒于嬉  阅读(2062)  评论(0编辑  收藏  举报