NET|C#生成PDF文件

项目需要在线生成PDF文件,我首先考虑采用itextsharp控件来实现。具体方法参考 https://sourceforge.net/projects/itextsharp/

1.首先利用nuget 引入该控件包。
image.png
2 然后直接创建就可以了。
using iTextSharp.text;
using iTextSharp.text.pdf;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;


public class CreatePDF
{
    private static CreatePDF instance;
    public static CreatePDF GetInstance()
    {
        if (instance == null)
        {
            instance = new CreatePDF();
        }
        return instance;
    }

    private static Document doc;
    //string fontdb = AppDomain.CurrentDomain.BaseDirectory + "Template\\msyh.ttc";

    //private static BaseFont bf = BaseFont.CreateFont(@"C://Windows/Fonts/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
    static string fontdb = (AppDomain.CurrentDomain.BaseDirectory + "Template\\msyh.ttf").Replace("\\", "/");
    private static BaseFont bf = BaseFont.CreateFont(fontdb + "", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
    //四种字体
    private static Font fontBig = new Font(bf, 14, Font.BOLD);

    private static Font fontSmall = new Font(bf, (float)10.5, Font.BOLD);
    private static Font fontSmallNoBold = new Font(bf, (float)10.5);
    private static float IndentationLeft = 50;//距左边距
    //如果要传参数进来,可自定义
    public string GeneratePDF(string name)
    {
        doc = new Document(PageSize.A4);
        string filePath = string.Empty;
        try
        {
            //MemoryStream ms2 = new MemoryStream();
            string fileName = string.Format("{0}.pdf", DateTime.Now.ToString("yyyyMMddHHmmss"));
            filePath = AppDomain.CurrentDomain.BaseDirectory + "Template\\" + fileName;
            FileStream fs = new FileStream(filePath, FileMode.Create);//创建临时文件,到时生成好后删除
            PdfWriter writer = PdfWriter.GetInstance(doc, fs);
            writer.CloseStream = false;//把doc内容写入流中
            doc.Open();

            //核心操作
            CreateLine(name);//生成一条下横线

            #region 添加水印
            string waterMarkName = "机密";

            #endregion

            doc.Close();
            MemoryStream ms = new MemoryStream();
            if (fs != null)
            {
                byte[] bytes = new byte[fs.Length];//定义一个长度为fs长度的字节数组
                fs.Read(bytes, 0, (int)fs.Length);//把fs的内容读到字节数组中
                ms.Write(bytes, 0, bytes.Length);//把字节内容读到流中
                fs.Flush();
                fs.Close();
            }
            MemoryStream waterMS = SetWaterMark(ms, filePath, waterMarkName);//先生成水印,再删除临时文件
            //if (File.Exists(filePath))//判断临时文件是否存在,如果存在则删除
            //{
            //    File.Delete(filePath);
            //    GC.Collect();//回收垃圾
            //}
            //SendFile(fileName, waterMS);//把PDF文件发送回浏览器
        }
        catch (DocumentException ex)
        {
            throw new Exception(ex.Message);
        }
        return filePath;
    }
    #region 生成一条横线
    private static void CreateLine(string name)
    {
        PdfPTable table = new PdfPTable(1);//一个单元格的

        PdfPCell cell = new PdfPCell();
        Image gif = Image.GetInstance(AppDomain.CurrentDomain.BaseDirectory + "Template\\logo.png");
        gif.ScaleAbsoluteWidth(100);
        cell.AddElement(gif);
        cell.BorderWidth = 0f;
        cell.BorderWidthBottom = 0.2f;
        table.AddCell(cell);

        doc.Add(table);
    }
    #endregion


    #region 生成页码
    private static void AddPageNumberContent()
    {
        var content = new Paragraph("共   页  第   页", fontSmall);
        content.IndentationRight = IndentationLeft + 20;
        content.Alignment = 2;    //居左
        doc.Add(content);
    }
    #endregion

    #region 生成单元格
    private static PdfPCell GetPdfCell(string content, Font font, int horizontalAlignment)
    {

        Paragraph paragraph = new Paragraph(content, font);
        paragraph.FirstLineIndent = 2;
        paragraph.SetLeading(10, 20);
        paragraph.SpacingAfter = 10;

        PdfPCell cell = new PdfPCell(paragraph);
        cell.HorizontalAlignment = horizontalAlignment;//水平位置
        cell.VerticalAlignment = Element.ALIGN_CENTER;//垂直居中
        cell.MinimumHeight = 20;//单元格的最小高度
        cell.Border = 0;
        return cell;
    }
    #endregion

    #region 生成水印
    private static MemoryStream SetWaterMark(MemoryStream ms, string filePath, string waterMarkName, string waterMarkAddr = null)
    {
        MemoryStream msWater = new MemoryStream();
        PdfReader pdfReader = null;
        PdfStamper pdfStamper = null;
        try
        {
            pdfReader = new PdfReader(filePath);
            pdfStamper = new PdfStamper(pdfReader, msWater);

            int total = pdfReader.NumberOfPages + 1;//获取PDF的总页数
            iTextSharp.text.Rectangle psize = pdfReader.GetPageSize(1);//获取第一页
            float width = psize.Width;//PDF页面的宽度,用于计算水印倾斜
            float height = psize.Height;
            PdfContentByte waterContent;
            BaseFont basefont = BaseFont.CreateFont(@"C:\WINDOWS\Fonts\SIMFANG.TTF", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            PdfGState gs = new PdfGState();
            for (int i = 1; i < total; i++)
            {
                waterContent = pdfStamper.GetOverContent(i);//在内容上方加水印
                //透明度
                waterContent.SetGState(gs);
                //开始写入文本
                waterContent.BeginText();
                waterContent.SetColorFill(BaseColor.RED);
                waterContent.SetFontAndSize(basefont, 18);
                waterContent.SetTextMatrix(0, 0);
                if (waterMarkAddr == null || waterMarkAddr == "")
                {
                    waterContent.ShowTextAligned(Element.ALIGN_CENTER, waterMarkName, width / 2, height / 2, 55);
                }
                else
                {
                    waterContent.ShowTextAligned(Element.ALIGN_CENTER, waterMarkName, width / 2, height / 2 + 100, 55);
                    waterContent.ShowTextAligned(Element.ALIGN_CENTER, waterMarkAddr, width / 2, height / 2 - 100, 55);
                }
                waterContent.EndText();
            }
        }
        catch (Exception)
        {
            return ms;
        }
        finally
        {
            if (pdfStamper != null)
                pdfStamper.Close();

            if (pdfReader != null)
                pdfReader.Close();
        }
        return msWater;
    }
    #endregion

    //-----------------------发送PDF文件回浏览器端----------------------
    public static void SendFile(string fileName, MemoryStream ms, Encoding encoding = null)
    {
        fileName = (fileName + "").Replace(" ", "");
        encoding = encoding ?? Encoding.UTF8;
        if (ms != null && !string.IsNullOrEmpty(fileName))
        {
            System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
            response.Clear();
            response.Charset = encoding.BodyName;// "utf-8";
            if (!HttpContext.Current.Request.UserAgent.Contains("Firefox") && !HttpContext.Current.Request.UserAgent.Contains("Chrome"))
            {
                fileName = HttpUtility.UrlEncode(fileName, encoding);
            }
            response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);
            //为了解决打开,导出NPOI生成的xlsx文件时,提示发现不可读取内容。
            if (!(fileName + "").ToLower().EndsWith(".xlsx"))
            {
                response.AddHeader("Content-Type", "application/octet-stream");
                response.BinaryWrite(ms.GetBuffer());
            }
            else
            {
                response.BinaryWrite(ms.ToArray());
            }
            ms.Close();
            ms = null;
            response.Flush();
            response.End();
        }
    }
}

特别需要注意一点的是,如果对字体有特殊要求,可以将字库文件放在项目目录下,但字库的文件类型不同,引用时略有差别。

ttc文件
BaseFont.CreateFont(@"C://Windows/Fonts/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
ttf文件
BaseFont.CreateFont(@"C://Windows/Fonts/simsun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

这种模式的弊端在于调整文档的格式太痛苦了,你想按照预定的模板生成pdf,格式要按照他的机制一点点写,如果是篇幅比较长的文档,耐心和时间缺一不可。

在我埋头写了一天之后,同事路过看到我一脸狰狞,细问之,马上丢给我一个包,十分钟后搞定。没有广告,工具类马上贴上来。

本方法依赖于Aspose.Words,因为版权问题,不要直接用nuget引入,需要去网上down一个破解版。然后将需要生成的文件内容置于一个word中,相应参数用字符标识后,方便替换。最后加入以下工具类直接调用就OK了。

(破解dll可以在这里自取 链接: https://pan.baidu.com/s/1o7XpCqI 密码: kwpt)

CrackWord.cs
using System;
using System.Reflection;

class CrackWord
{
    /// <summary>
    /// 16.10.1.0
    /// </summary>
    public static void Crack()//使用前调用一次即可
    {
        string[] stModule = new string[8]
            {
                "\u000E\u2008\u200A\u2001",
                "\u000F\u2008\u200A\u2001",
                "\u0002\u200A\u200A\u2001",
                "\u000F",
                "\u0006",
                "\u000E",
                "\u0003",
                "\u0002"
            };
        Assembly assembly = Assembly.GetAssembly(typeof(Aspose.Words.License));

        Type typeLic = null, typeIsTrial = null, typeHelper = null;
        foreach (Type type in assembly.GetTypes())
        {
            if ((typeLic == null) && (type.Name == stModule[0]))
            {
                typeLic = type;
            }
            else if ((typeIsTrial == null) && (type.Name == stModule[1]))
            {
                typeIsTrial = type;
            }
            else if ((typeHelper == null) && (type.Name == stModule[2]))
            {
                typeHelper = type;
            }
        }
        if (typeLic == null || typeIsTrial == null || typeHelper == null)
        {
            throw new Exception();
        }
        object lic = Activator.CreateInstance(typeLic);
        int findCount = 0;

        foreach (FieldInfo field in typeLic.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
        {
            if (field.FieldType == typeLic && field.Name == stModule[3])
            {
                field.SetValue(null, lic);
                ++findCount;
            }
            else if (field.FieldType == typeof(DateTime) && field.Name == stModule[4])
            {
                field.SetValue(lic, DateTime.MaxValue);
                ++findCount;
            }
            else if (field.FieldType == typeIsTrial && field.Name == stModule[5])
            {
                field.SetValue(lic, 1);
                ++findCount;
            }

        }
        foreach (FieldInfo field in typeHelper.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
        {
            if (field.FieldType == typeof(bool) && field.Name == stModule[6])
            {
                field.SetValue(null, false);
                ++findCount;
            }
            if (field.FieldType == typeof(int) && field.Name == stModule[7])
            {
                field.SetValue(null, 128);
                ++findCount;
            }
        }
        if (findCount < 5)
        {
            throw new NotSupportedException("无效的版本");
        }
    }
}

ReplaceAndInsertImage.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Aspose.Words;
using Aspose.Words.Replacing;

public class ReplaceAndInsertImage : IReplacingCallback
{
    /// <summary>
    /// 需要插入的图片路径
    /// </summary>
    public string Url { get; set; }

    public ReplaceAndInsertImage(string url)
    {
        this.Url = url;
    }

    public ReplaceAction Replacing(ReplacingArgs e)
    {
        //获取当前节点
        var node = e.MatchNode;
        //获取当前文档
        Document doc = node.Document as Document;
        DocumentBuilder builder = new DocumentBuilder(doc);
        //将光标移动到指定节点
        builder.MoveTo(node);
        //插入图片
        builder.InsertImage(Url);
        return ReplaceAction.Replace;
    }
}
Cv.cs 调用生成类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Aspose.Words;

public class Cv
{
    /// <summary>
    /// 生成录用offer文档
    /// </summary>
    /// <param name="source">模板文件</param>
    /// <param name="filespath">生成的PDF目录</param>
    /// <returns></returns>
    public static void Run(string source, string filespath,)
    {

        var dic = new Dictionary<string, string>();
       //替换模板文件的参数
        dic.Add("{xingming}","参数");
     
        CrackWord.Crack();
        Aspose.Words.Document doc = new Aspose.Words.Document(source);

        foreach (var key in dic.Keys)
        {
#pragma warning disable 618
            doc.Range.Replace(key, dic[key], false, false);
#pragma warning restore 618
        }

        doc.Save(filespath, SaveFormat.Pdf);
       
    }
}

source 为模板文件目录,本例中为word 文件。

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容