SkiaSharp跨平台繪圖研究4-在PDF上繪圖
在很多應用場景中,軟件系統是要有打印報告功能的,要知道SkiaSharp還能夠在PDF上繪圖,都不知道該怎么誇它了,一套繪圖代碼,可以在PC端,移動端,Linux雲服務器到處用,還能導出PDF,打印到A4紙,這跨平台能力也太強大了。
繼續沿用之前的Asp.Net Core項目,在網頁上增加一個導出PDF報告的功能,然后創建一個PDF文件,可以下載,也可以直接打印。
在PDF上繪圖
在PDF上繪圖的代碼跟WPF項目基本一樣。
using SkiaSharp; namespace WebMvcDemo { /// <summary> /// 創建PDF繪圖文件 /// </summary> public class SkiaPdfMaker { /// <summary> /// 每毫米對應多少打印點數 = 每英寸打印多少點數 / 每英寸多少毫米 = 2.83464567 /// Dpi打印分辨率,Dot Per Inch,每英寸打印點數,默認72 /// 1英寸=25.4毫米 /// </summary> public const float DotPermm = SKDocument.DefaultRasterDpi / 25.4f; /// <summary> /// 導出PDF文件 /// </summary> /// <returns></returns> public byte[] ExportToPdf() { Console.WriteLine($"{DateTime.Now}, 開始導出PDF"); using MemoryStream stream = new MemoryStream(); var pdfMetadata = new SKDocumentPdfMetadata { Author = "SkiaPdfMaker", }; //初始化一個PdfDocument類實例 using SKDocument pdfDocument = SKDocument.CreatePdf(stream, pdfMetadata); //新添加一頁紙張,創建紙張大小的畫布,A4尺寸 = 210 X 297 mm float paperWidth = DotPermm * 210; float paperHeight = DotPermm * 297; var canvas = pdfDocument.BeginPage(paperWidth, paperHeight); using var paint = new SKPaint { Color = SKColors.Black, IsAntialias = true, Typeface = SkiaChinaFont.ChinaFont, TextSize = 24 }; string msg = $"{DateTimeOffset.Now:T}, 還有1萬行Skia繪圖代碼..."; canvas.DrawText(msg, 0, 30, paint); using var linePaint = new SKPaint() { Color = (DateTimeOffset.Now.Second % 4 <= 1) ? SKColors.Red : SKColors.Green, Style = SKPaintStyle.Stroke,//不填充 StrokeWidth = 3, }; canvas.DrawRect(10, 50, paperWidth - 20, paperHeight - 60, linePaint); msg += $", linePaint.Color={linePaint.Color}, skContainer.CanvasSize={paperWidth} x {paperHeight}"; Console.WriteLine(msg); //結束一頁內容 pdfDocument.EndPage(); //結束文檔內容 pdfDocument.Close(); byte[] ary = stream.ToArray(); return ary; } } }
在頁面下載PDF
控制器Action就是返回一個PDF文件類型
/// <summary> /// 導出Skia繪圖PDF文件 /// </summary> /// <returns></returns> public IActionResult ExportSkiaPdf() { //導出PDF文件 var skiaPdfMaker = new SkiaPdfMaker(); var ary = skiaPdfMaker.ExportToPdf(); string httpHeader = $"attachment; filename={DateTime.Now:yyyyMMdd-HHmmss}.pdf"; HttpContext.Response.Headers.Add("content-disposition", httpHeader); return File(ary, "application/pdf"); }
主頁增加一個超級鏈接按鈕
<a class="btn btn-info m-1" href="/Home/ExportSkiaPdf" target="_blank">導出PDF</a>
運行網站,在瀏覽器中點擊【導出PDF】超級鏈接,就會下載創建的PDF文件。
發布到騰訊雲CentOS操作系統容器里運行也一樣的結果。
該方案存在的問題
這個如此簡單的PDF文件居然有1.7M字節,我不知道該說什么好了。為什么會這樣,我也不知道。我曾經對比過其他PDF生成方案創建的PDF文件,比SkiaSharp小多了!SkiaSharp生成的PDF如果不使用中文字庫,仍然有500k字節,貌似SkiaSharp把整個中文字庫全部嵌入到PDF里邊去了。
使用老牌iTextSharp創建PDF方案做個對比,NuGet安裝iTextSharp.LGPLv2.Core,這個是iText免費版的絕版。
<PackageReference Include="iTextSharp.LGPLv2.Core" Version="1.7.5" />
使用iTextSharp創建同樣的PDF文件,iText的坐標系是倒置的,繪圖代碼跟SkiaSharp不能共用。
using iTextSharp.text; using iTextSharp.text.pdf; namespace WebMvcDemo { /// <summary> /// 創建PDF繪圖文件 /// </summary> public class ItextPdfMaker { /// <summary> /// 導出PDF文件 /// </summary> /// <returns></returns> public byte[] ExportToPdf() { Console.WriteLine($"{DateTime.Now}, iText開始導出PDF"); using MemoryStream stream = new MemoryStream(); //初始化一個Document類實例,創建紙張大小的畫布,A4尺寸 = 210 X 297 mm,= 595 x 842,設置頁邊距全0,整個頁面全部作為畫布 var document = new Document(PageSize.A4, 0, 0, 0, 0); //必須先獲取PdfWriter,然后document.Open,如果交換順序,報錯Document not open PdfWriter writer = PdfWriter.GetInstance(document, stream); document.AddAuthor("ItextPdfMaker"); document.AddCreator("ItextPdfMaker"); document.Open(); var canvas = writer.DirectContent; var textfont = new Font(ItextChinaFont.ChinaFont) { Color = BaseColor.Black, Size = 24 }; string msg = $"{DateTimeOffset.Now:T}, 還有1萬行iText繪圖代碼..."; ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, new Phrase(msg, textfont), 0, canvas.PdfDocument.Top - 30, 0); var lineColor = (DateTimeOffset.Now.Second % 4 <= 1) ? BaseColor.Red : BaseColor.Green; canvas.SetColorStroke(lineColor); canvas.SetLineWidth(3); //左邊-底部-右邊-頂部 canvas.Rectangle(10, canvas.PdfDocument.Top - (PageSize.A4.Height - 60), PageSize.A4.Width - 20, canvas.PdfDocument.Top - 120); canvas.Stroke(); msg += $", lineColor={lineColor.ToArgb():X2}, CanvasSize={PageSize.A4}"; Console.WriteLine(msg); //結束一頁內容 //document.NewPage(); //結束文檔內容 document.Close(); byte[] ary = stream.ToArray(); return ary; } } }
創建中文字體類
using iTextSharp.text.pdf; namespace WebMvcDemo { /// <summary> /// iText中文字體 /// </summary> public static class ItextChinaFont { public static BaseFont ChinaFont { get; private set; } static ItextChinaFont() { //加載字體資源文件方案,需要把字體文件復制運行目錄下,設置文件屬性為如果較新則復制 string fontPath = Path.Combine(AppContext.BaseDirectory, "DroidSansFallback.ttf"); ChinaFont = BaseFont.CreateFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED); } } }
iTextSharp創建的PDF才40k字節,部署到騰訊雲CentOS容器也沒有什么依賴庫。所以最終我沒有把SkiaSharp方案用於產品中,仍然使用了老掉牙的iTextSharp免費版本,真是非常遺憾。希望將來能找到問題原因,重新把SkiaSharp創建PDF用起來。
DEMO源代碼參見:https://gitee.com/woodsun/skia-sharp-demo