zoukankan      html  css  js  c++  java
  • SkiaSharp跨平台绘图研究4在PDF上绘图

    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创建的PDF40k字节,部署到腾讯云CentOS容器也没有什么依赖库。所以最终我没有把SkiaSharp方案用于产品中,仍然使用了老掉牙的iTextSharp免费版本,真是非常遗憾。希望将来能找到问题原因,重新把SkiaSharp创建PDF用起来。

    DEMO源代码参见:https://gitee.com/woodsun/skia-sharp-demo

  • 相关阅读:
    使用Eclipse创建Maven的JSP项目
    MySQL远程访问
    IDEA创建web工程,不用Archetype(超简单)
    IDEA创建web工程(超简单)
    共享软件
    C语言讲义——链表完整代码
    base64图片显示问题
    JAVA 判断一个字符串是否是合法的日期格式?
    SpringBoot配置本地文件映射路径
    SpringBoot读取资源目录下的文件
  • 原文地址:https://www.cnblogs.com/sunnytrudeau/p/15570085.html
Copyright © 2011-2022 走看看