按照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的解釋就是 在頁面刷新到文檔之前調度的事件類型)。效果如下圖,背景色按照奇數檸檬黃、偶數綠色設置,但是其中有一個問題,除了第一頁第一行有圓角設置外后面頁的第一行標題沒有格式,這個問題抽時間解決后再來更新一下文章,如果知道的小伙伴在評論里留言指出我會非常感謝。
如果該文章對你有所幫助,還望推薦一下。如果慷慨的你願意打賞,那就再好不過了。