zoukankan      html  css  js  c++  java
  • C#自定义TemplateImage使用模板底图,运行时根据用户或产品信息生成海报图(1)

    由于经常需要基于固定的一个模板底图,生成微信小程序分享用的海报图,如果每次都调用绘图函数,手动编写每个placeholder的填充,重复而且容易出错,因此,封装一个TemplateImage,用于填充每个需要画上数据的地方,

    先看看调用的方式:

    _homeShareTemplate.Generate(new TemplateItem[]   //Generate返回新的Bitmap
                {
                    new StringTemplateItem() //日期
                    {
                        Location = new Point(80 * 2, 78*2),
                        Font = new Font("宋体", 42, FontStyle.Bold, GraphicsUnit.Pixel),
                        Color = Color.FromArgb(0x8e, 0x1a, 0x22),
                        Value = DateTime.Now.ToString("yyyy.MM.dd"),
                        Horizontal = HorizontalPosition.Center
                    },
                    new StringTemplateItem() //农历
                    {
                        Location = new Point(230*2, 166*2),
                        //MaxWidth = 15,
                        Font = new Font("宋体", 22, FontStyle.Bold, GraphicsUnit.Pixel),
                        Color = Color.FromArgb(0x8e, 0x1a, 0x22),
                        StringFormat = new StringFormat(StringFormatFlags.DirectionVertical),
                        Value = GetMonthCalendar(DateTime.Now)
                    },
                    new StringTemplateItem() //星期
                    {
                        Location = new Point(256*2, 175*2),
                        //MaxWidth = 15,
                        Font = new Font("宋体", 24, FontStyle.Bold, GraphicsUnit.Pixel),
                        Color = Color.FromArgb(0x8e, 0x1a, 0x22),
                        StringFormat = new StringFormat(StringFormatFlags.DirectionVertical),
                        Value = GetWeekName(DateTime.Now)
                    },
                    new ImageTemplateItem() //图片
                    {
                        Image = (Bitmap) Bitmap.FromFile(Path.Join(Directory.GetCurrentDirectory(),weather.MainImageUrl)),
                        Location = new Point(81*2, 108*2),
                        Size = new Size(132*2, 133*2)
                    },
                    new StringTemplateItem()
                    {
                        Location = new Point(88*2, 257*2),
                        MaxWidth = 125*2,
                        Font = new Font("楷体", 30, FontStyle.Bold, GraphicsUnit.Pixel),
                        Color = Color.FromArgb(0x17, 0x14, 0x0e),
                        Value = weather.Content.Left(44)
                    },
                    new StringTemplateItem() //
                    {
                        Location = new Point(35*2+3,294*2),
                        Color = Color.FromArgb(0x8f, 0x1A, 0x22),
                        Font = new Font("宋体", 38, FontStyle.Bold, GraphicsUnit.Pixel),
                        StringFormat = new StringFormat(StringFormatFlags.DirectionVertical),
                        //MaxWidth = 14,
                        Value = weather.Yi.Left(4)
                    },
                    new StringTemplateItem() //
                    {
                        Location = new Point(228*2+3,294*2),
                        Color = Color.FromArgb(0x8f, 0x1A, 0x22),
                        Font = new Font("宋体", 38, FontStyle.Bold, GraphicsUnit.Pixel),
                        StringFormat = new StringFormat(StringFormatFlags.DirectionVertical),
                        //MaxWidth = 14,
                        Value = weather.Ji.Left(4)
                    },
                    new QrCodeTemplateItem() //二维码
                    {
                        Location = new Point(188*2, 421*2),
                        Size = new Size(73*2, 72*2),
                        QrCode = "http://ssssss.com/sdfsdfsdfs/sss"
                    }
                });

    输出的效果如下:

    完整的功能由一个TemplateImage作为模板图管理的类+N个根据需要输出的各种数据处理类,可根据实际需求进行扩展不同的类型,默认有:String,Image,QrCode三种:

    单个模板图管理类的定义:

    public class TemplateImage:IDisposable
    {
            private Bitmap _templateSource = null;
            private Stream _sourceStream = null;
            private FileSystemWatcher _wather = null;
    
            public TemplateImage(Bitmap templateSource)
            {
                _templateSource = templateSource;
            }
    
            /// <summary>
            /// 模板图片的构造函数
            /// </summary>
            /// <param name="templatePath">模板图片文件绝对路径</param>
            /// <param name="isWatchFileModify">是否自动监控文件,当文件有变动时,自动重新加载模板文件
            /// </param>
            public TemplateImage(string templatePath,bool isWatchFileModify=true)
            {
                if (!File.Exists(templatePath))
                {
                    throw new FileNotFoundException(nameof(templatePath));
                }
    
                //打开模板文件路径,在跳出构造函数后,自动释放file对象,防止长久占用文件,导致无法替换模板文件
                using var file = File.OpenRead(templatePath);
    
                var data = file.ReadAllBytes(); 
    
                var s1 = new ByteStream(data);  //这里s1肯定不能关闭,否则,再调用Bitmap.Clone函数的时候,会报错
                _sourceStream = s1;
                _templateSource = (Bitmap) Bitmap.FromStream(s1);
    
                if (isWatchFileModify) //如果启用文件监控,则自动监控模板图片文件
                {
                    _wather = new FileSystemWatcher(templatePath);
                    _wather.EnableRaisingEvents = true;
                    _wather.Changed += wather_changed;
    
                }
            }
    
            private void wather_changed(object sender, FileSystemEventArgs e)
            {
                if (e.ChangeType == WatcherChangeTypes.Changed || e.ChangeType== WatcherChangeTypes.Created )
                {
                    using var file = File.OpenRead(e.FullPath);
    
                    var data = file.ReadAllBytes();
    
                    var oldValue = _sourceStream;
                    var templateSource = _templateSource;
                    var s1 = new ByteStream(data);
                    var newTemplateSource = (Bitmap) Bitmap.FromStream(s1);
                    
                    _sourceStream = s1;
                    _templateSource = newTemplateSource;
                    
                    oldValue.Close();
                    oldValue.Dispose();
                    templateSource.Dispose();
                }
            }
            
    
            public SmoothingMode SmoothingMode { set; get; } = SmoothingMode.AntiAlias;
    
            public TextRenderingHint TextRenderingHint { set; get; } = TextRenderingHint.AntiAlias;
    
            public CompositingQuality CompositingQuality { set; get; } = CompositingQuality.HighQuality;
    
            /// <summary>
            /// 根据传入的数据,套入模板图片,生成新的图片
            /// </summary>
            /// <param name="settings"></param>
            /// <returns></returns>
            public Bitmap Generate(TemplateItemBase[] settings)
            {
                //Clone一个新的Bitmap对象
                var newImg = (Bitmap)_templateSource.Clone();
                
                var g1 = Graphics.FromImage(_templateSource);
                
                try
                {
                    using (var g = Graphics.FromImage(newImg))
                    {
                        g.SmoothingMode = SmoothingMode;
                        g.TextRenderingHint = TextRenderingHint;
                        g.CompositingQuality = CompositingQuality;
                
                        foreach (var item in settings)
                        {
                            item.Draw(g, newImg.Size); //调用每个Item的Draw画入新的数据
                        }
    
                        return newImg;
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
                
            }
                    
            public void Dispose()
            {
                _templateSource.Dispose();
                
                _sourceStream?.Close();
                _sourceStream?.Dispose();
            }
        }    

    至此,一个模板图片类已定义完成,接下来需要定义一个Placeholder的基类:

     1     public abstract class TemplateItemBase
     2     {
     3         /// <summary>
     4         /// 水平方向对其方式,默认为Custom,使用Location定位
     5         /// </summary>
     6         public HorizontalPosition Horizontal { set; get; } = HorizontalPosition.Custom;
     7 
     8         /// <summary>
     9         /// 垂直方向对其方式,默认为Custom,使用Location定位
    10         /// </summary>
    11         public VerticalPosition Vertical { set; get; } = VerticalPosition.Custom;
    12      
    13         /// <summary>
    14         /// 输出项定位
    15         /// </summary>
    16         public Point Location { set; get; }
    17 
    18         public abstract void Draw(Graphics graphics,Size newBitmapSize);
    19 
    20     }

    这个基类定义了每个placeholder的定位方式,Custom表示使用Location自定义位置.

    然后开始来定义每个不同类型的TemplateItem:

    1.String类型:

     1     /// <summary>
     2     /// 普通字符串项
     3     /// </summary>
     4     public class StringTemplateItem : TemplateItemBase
     5     {
     6         /// <summary>
     7         /// 文本字符串值
     8         /// </summary>
     9         public string Value { set; get; }
    10         
    11         /// <summary>
    12         /// 字体信息
    13         /// </summary>
    14         public Font Font { set; get; }
    15         
    16         /// <summary>
    17         /// 字体颜色
    18         /// </summary>
    19         public Color Color { set; get; }= Color.Black;
    20 
    21         /// <summary>
    22         /// 文本输出的最大宽度,如果为0,则自动,,如果非0,则只用最大宽度,并自动根据最大宽度修改计算字符串所需高度
    23         /// </summary>
    24         public int MaxWidth { set; get; } = 0;
    25 
    26         /// <summary>
    27         /// 字符串输出参数
    28         /// </summary>
    29         /// <example>
    30         /// 如纵向输出:
    31         /// new StringFormat(StringFormatFlags.DirectionVertical)
    32         /// 
    33         /// </example>
    34         public StringFormat StringFormat { set; get; }
    35 
    36         public override void Draw(Graphics graphics,Size newBitmapSize)
    37         {
    38             var location = this.Location;
    39             SizeF size=default(Size);
    40             if (this.Horizontal== HorizontalPosition.Center || this.Vertical== VerticalPosition.Middle)
    41             {
    42                 location = new Point(this.Location.X,this.Location.Y);
    43                 
    44                 if (this.MaxWidth>0)
    45                 {
    46                     size = graphics.MeasureString(this.Value, this.Font,this.MaxWidth);
    47                 }
    48                 else
    49                 {
    50                     size = graphics.MeasureString(this.Value, this.Font);
    51                 }
    52                         
    53                 if (this.Horizontal== HorizontalPosition.Center)
    54                 {
    55                     var newx = newBitmapSize.Width / 2 - (int)(size.Width / 2);
    56                     location.X = newx;
    57                 }
    58 
    59                 if (this.Vertical== VerticalPosition.Middle)
    60                 {
    61                     var newy= newBitmapSize.Height / 2 - (int)(size.Height / 2);
    62                     location.Y = newy;
    63                 }
    64             }
    65             else if(MaxWidth>0)
    66             {
    67                 size = graphics.MeasureString(this.Value, this.Font,this.MaxWidth);
    68             }
    69 
    70             if (MaxWidth>0)
    71             {
    72                 graphics.DrawString(this.Value, this.Font,new SolidBrush(this.Color), new RectangleF(location,size),StringFormat);
    73             }
    74             else
    75             {
    76                 graphics.DrawString(this.Value, this.Font,new SolidBrush(this.Color), location,StringFormat);
    77             }
    78             
    79             
    80         }
    81     }

    2.纯图片类型:

     1     /// <summary>
     2     /// 传入一个图片
     3     /// </summary>
     4     public class ImageTemplateItem:TemplateItemBase
     5     {
     6         /// <summary>
     7         /// 图片数据
     8         /// </summary>
     9         public Bitmap Image { set; get; }
    10         
    11         /// <summary>
    12         /// 图片输出到模板图的时候的大小
    13         /// </summary>
    14         public Size Size { set; get; }
    15         
    16         public override void Draw(Graphics graphics,Size newBitmapSize)
    17         {
    18             var location = this.Location;
    19             
    20             //计算垂直居中或水平居中的情况下的定位
    21             if (this.Horizontal== HorizontalPosition.Center || this.Vertical== VerticalPosition.Middle)
    22             {
    23                 location = new Point(this.Location.X,this.Location.Y);
    24                         
    25                 if (this.Horizontal== HorizontalPosition.Center)
    26                 {
    27                     var newx = newBitmapSize.Width / 2 - this.Size.Width / 2;
    28                     
    29                     location.X = newx;
    30                 }
    31 
    32                 if (this.Vertical== VerticalPosition.Middle)
    33                 {
    34                     var newy= newBitmapSize.Height / 2 - this.Size.Height / 2;
    35                     location.Y = newy;
    36                 }
    37             }
    38             
    39             //此处后续可优化为使用Lockbits的方式
    40             graphics.DrawImage(Image,new Rectangle(location,this.Size),new Rectangle(0,0,this.Image.Width,this.Image.Height),GraphicsUnit.Pixel);
    41             
    42         }
    43     }

     3.QrCode的方式,使用QRCoder类库:

     1     /// <summary>
     2     /// 二维码项
     3     /// </summary>
     4     public class QrCodeTemplateItem : TemplateItemBase
     5     {
     6         /// <summary>
     7         /// 二维码内实际存储的字符数据
     8         /// </summary>
     9         public string QrCode { set; get; }
    10         
    11         /// <summary>
    12         /// 二维码中心的icon图标
    13         /// </summary>
    14         public Bitmap Icon { set; get; }
    15         
    16         /// <summary>
    17         /// 二维码尺寸
    18         /// </summary>
    19         public Size Size { set; get; }
    20 
    21         /// <summary>
    22         /// 容错级别,默认为M
    23         /// </summary>
    24         public QRCodeGenerator.ECCLevel ECCLevel { set; get; } = QRCodeGenerator.ECCLevel.M;
    25         
    26         public override void Draw(Graphics graphics,Size newBitmapSize)
    27         {
    28             var location = this.Location;
    29             
    30             if (this.Horizontal== HorizontalPosition.Center || this.Vertical== VerticalPosition.Middle)
    31             {
    32                 location = new Point(this.Location.X,this.Location.Y);
    33                         
    34                 if (this.Horizontal== HorizontalPosition.Center)
    35                 {
    36                     var newx = newBitmapSize.Width / 2 - this.Size.Width / 2;
    37                     
    38                     location.X = newx;
    39                 }
    40 
    41                 if (this.Vertical== VerticalPosition.Middle)
    42                 {
    43                     var newy= newBitmapSize.Height / 2 - this.Size.Height / 2;
    44                     location.Y = newy;
    45                 }
    46             }
    47             
    48             using (QRCodeGenerator qrGenerator = new QRCodeGenerator())
    49             using (QRCodeData qrCodeData = qrGenerator.CreateQrCode(QrCode,ECCLevel))
    50             using (QRCode qrCode = new QRCode(qrCodeData))
    51             using (Bitmap qrCodeImage = qrCode.GetGraphic(20,Color.Black,Color.White,Icon))
    52             {
    53                 graphics.DrawImage(qrCodeImage,new Rectangle(location,this.Size),new Rectangle(0,0,qrCodeImage.Width,qrCodeImage.Height),GraphicsUnit.Pixel);
    54                 
    55             }
    56         }
    57     }

    后续的优化:

    1.Image画入的优化处理,考虑是否可以用Lockbits进行优化

    2.增加不同类型的新的Item

    完整的代码详见:https://github.com/kugarliyifan/Kugar.Core/blob/master/Kugar.Core.NetCore/Images/TemplateImage.cs

  • 相关阅读:
    hdu 5723 Abandoned country 最小生成树 期望
    OpenJ_POJ C16G Challenge Your Template 迪杰斯特拉
    OpenJ_POJ C16D Extracurricular Sports 打表找规律
    OpenJ_POJ C16B Robot Game 打表找规律
    CCCC 成都信息工程大学游记
    UVALive 6893 The Big Painting hash
    UVALive 6889 City Park 并查集
    UVALive 6888 Ricochet Robots bfs
    UVALive 6886 Golf Bot FFT
    UVALive 6885 Flowery Trails 最短路
  • 原文地址:https://www.cnblogs.com/kugar/p/14209584.html
Copyright © 2011-2022 走看看