草庐IT

利用FastReport传递图片参数,在报表上展示签名信息

wuhuacong(伍华聪)的专栏 2023-03-28 原文

在一个项目中,客户要求对报表中的签名进行仿手写的签名处理,因此我们原先只是显示相关人员的姓名的地方,需要采用手写方式签名,我们的报表是利用FastReport处理的,在利用楷体处理的时候,开发展示倒是正常效果,不过实际上在服务器运行的时候,出来的确实正规的宋体格式,相应的字体都已经安装,不过还是没有生效。因此采用变通的方式,在对应签名的地方采用图片的格式显示,实际效果达到要求。本篇随笔介绍这个过程,利用FastReport传递图片参数,在报表上展示签名信息的处理。

1、报表上的设计处理

例如我们要在报表落款的附近记录相关人员的名字,因此采用签名的显示方式会比较合理。

 

因此设计相关的报表,本来想是采用文本的方式,变化字体的方式来快捷实现的

 

 不过在报表是在服务器上生成图片的方式,导致正常出来的图片,却没有变化字体,导致出来的是正常的宋体格式。 

  

 因此考虑使用图片的格式方式来处理。在其中放置Picture控件,如下所示。

 

调整好Picture控件的高度和宽度,让它在设计的空白上合适的展示即可。 

为了实现图片格式的显示,我们需要在报表的图片控件的BeforePrint事件中解析数据(来自传递参数),数据格式为Base64字符串(从Byte数据转换),如下代码所示。

namespace FastReport
{
  public class ReportScript
  {

    private void shopDoctorImg_BeforePrint(object sender, EventArgs e)
    {
      var img = GetImage("ShopDoctor");
      if(img != null){
        shopDoctorImg.Image=img;  
      }
    }   
    private void tiaopeiImg_BeforePrint(object sender, EventArgs e)
    {    
      var img = GetImage("Tiaopei");
      if(img != null){
        tiaopeiImg.Image=img;  
      }
    }       

    private void CheckDoctorImg_BeforePrint(object sender, EventArgs e)
    {
      var img = GetImage("CheckDoctor");
      if(img != null){
        CheckDoctorImg.Image=img;  
      }
    }

    private void CheckPharmacistImg_BeforePrint(object sender, EventArgs e)
    {       
      var img = GetImage("CheckPharmacist");
      if(img != null){
        CheckPharmacistImg.Image=img;  
      }
    }
    private Image GetImage(string signName)
    {         
      Image img = null;
      string imgStr = (string)Report.GetParameterValue(signName);
      if(!string.IsNullOrEmpty(imgStr))
      {
        byte[] imgData= Convert.FromBase64String(imgStr);
        using(MemoryStream ms = new MemoryStream(imgData))
        {
          img = System.Drawing.Image.FromStream(ms);            
        }
      }
      return img;
    }
        
  }
}

其中主要注意的是,我们传递的图片数据需要采用Base64String的格式才能正常传递和展示。 

2、报表传递图片数据

完成了报表的设计处理,我们剩下的就是在实际的报表中传递对应的参数数据了。

我们把签名图片,放在相对的目录上,如下所示。

 

 然后编写一个公用的读取图片为Base64String的函数处理,如下所示。

        //通过姓名获取签名图片的Base64
        private string GetSignImage(string signName)
        {
            var result = "";
            string imagePath = Path.Combine(baseDir, $"Report/signs/{signName}.png");
            if (File.Exists(imagePath))
            {
                var stream = FileUtil.FileToStream(imagePath);
                var image = FileUtil.StreamToBytes(stream);
                if (image != null)
                {
                    result = Convert.ToBase64String(image);
                }
            }
            return result;
        }

接着就是根据对应的报表进行加载,并设置相关的参数进行传递给报表即可,如下测试代码所示。

    //生成PDF报表文档到具体文件
    Report report = new Report();
    report.Load(reportFile);

    //定义参数和数据格式
    var dict = new Dictionary<string, object>();
    #region 测试数据源
    dict.Add("Name", "张三");
    dict.Add("Gender", "");
    dict.Add("Age", 32);
    dict.Add("Telephone", "18620292076");
    dict.Add("CreateTime", "2019-10-13 22:30:15");
    dict.Add("CheckDoctor", GetSignImage("张医生"));//"张医生"
    dict.Add("CheckPharmacist", GetSignImage("张医生")); //"李药师"
    dict.Add("SendUser", "王小姐");
    dict.Add("QrCode", "http://www.iqidi.com");
    dict.Add("BarCode", "1234567890");

    //图片文件
    dict.Add("ShopDoctor", GetSignImage("张医生"));
    dict.Add("Tiaopei", GetSignImage("张医生"));
    dict.Add("Fayao", GetSignImage("王小姐"));    
    #endregion
    
    report.RegisterData(dt, "Detail");
    foreach (string key in dict.Keys)
    {
        report.SetParameterValue(key, dict[key]);
    }

    //运行报表
    report.Prepare();    

由于我们的报表,最终是生成PDF或者图片的方式,方便客户进行在线查询的,因此可以选择PDF或者图片的格式生成。

    //运行报表
    report.Prepare();    
    
    //导出PDF报表
    //PDFExport export = new PDFExport();

    //多个图片导出
    int count = 1;
    string firstFileName = exportImgPath.Replace(".png", "");
    foreach (PageBase item in report.Pages)
    {
        string fileName = string.Format("{0}_{1}.png", firstFileName,  count);
        exportImgPath = fileName;
        //Resolution= 300可以提高分辨率
        report.Export(new ImageExport() { PageRange = PageRange.Current, CurPage = count, Resolution= 300 }, fileName);
        count++;
    }

最后生成的图片格式如下所示,顺利吧签名的图片贴在对应的单元格中即可。

 

有关利用FastReport传递图片参数,在报表上展示签名信息的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  4. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  5. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  6. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  7. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  8. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  9. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

  10. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

随机推荐