Search code examples
c#pdfcontenteditablehtml-to-pdf

Converting contenteditable content to PDF


I would like to know the most efficient way to convert contenteditable features (something that the user puts in) to an pdf. Here is an illustration of what i mean:

1.

User inputs something in div contenteditable

2

Then download the pdf

3

And lastly the text that has been put into the pdf

I would also like to know how to convert css features since jsPDF doesn't suppoert this (to my knowledge)


Solution

  • jsPDF doesn't support almost the features what you need. I suggest to create an application to do that.

    My background is C#. So:

    Program.cs

    using HtmlToPdf.Models;
    
    namespace HtmlToPdf.Console
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                var model = new HtmlToPdfModel();
                model.HTML = "<h3>Hello world!</h3>";
                model.CSS = "h3{color:#f00;}";
    
                HtmlToPdf.Convert(model);
            }
        }
    }
    

    HtmlToPdfModel.cs

    namespace HtmlToPdf.Models
    {
        public class HtmlToPdfModel
        {
            public string HTML { get; set; }
    
            public string CSS { get; set; }
    
            public string OutputPath { get; set; }
    
            public string FontName { get; set; }
    
            public string FontPath { get; set; }
        }
    }
    

    HtmlToPdf.cs

    using iTextSharp.text;
    using iTextSharp.text.pdf;
    using iTextSharp.tool.xml;
    using HtmlToPdf.Models;
    using System;
    using System.IO;
    using System.Text;
    
    namespace HtmlToPdf.Console
    {
        public class HtmlToPdf
        {
            public static void Convert(HtmlToPdfModel model)
            {
                try
                {
                    if (model == null) return;
    
                    Byte[] bytes;
    
                    //Boilerplate iTextSharp setup here
                    //Create a stream that we can write to, in this case a MemoryStream
                    using (var stream = new MemoryStream())
                    {
                        //Create an iTextSharp Document which is an abstraction of a PDF but **NOT** a PDF
                        using (var doc = new Document())
                        {
                            //Create a writer that's bound to our PDF abstraction and our stream
                            using (var writer = PdfWriter.GetInstance(doc, stream))
                            {
                                //Open the document for writing
                                doc.Open();
    
                                //In order to read CSS as a string we need to switch to a different constructor
                                //that takes Streams instead of TextReaders.
                                //Below we convert the strings into UTF8 byte array and wrap those in MemoryStreams
                                using (var cssStream = new MemoryStream(Encoding.UTF8.GetBytes(model.CSS)))
                                {
                                    using (var htmlStream = new MemoryStream(Encoding.UTF8.GetBytes(model.HTML)))
                                    {
                                        var fontProvider = new XMLWorkerFontProvider();
    
                                        if (!string.IsNullOrEmpty(model.FontPath) && !string.IsNullOrEmpty(model.FontName))
                                        {
                                            fontProvider.Register(model.FontPath, model.FontName);
    
                                            //Parse the HTML with css font-family
                                            XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, htmlStream, cssStream, Encoding.UTF8, fontProvider);
                                        }
                                        else
                                        {
                                            //Parse the HTML without css font-family
                                            XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, htmlStream, cssStream);
                                        }
                                    }
                                }
    
                                doc.Close();
                            }
                        }
    
                        //After all of the PDF "stuff" above is done and closed but **before** we
                        //close the MemoryStream, grab all of the active bytes from the stream
                        bytes = stream.ToArray();
                    }
    
                    //Now we just need to do something with those bytes.
                    //Here I'm writing them to disk but if you were in ASP.Net you might Response.BinaryWrite() them.
                    //You could also write the bytes to a database in a varbinary() column (but please don't) or you
                    //could pass them to another function for further PDF processing.
    
                    // use this line on Windows version
                    //File.WriteAllBytes(model.OutputPath, bytes);
    
                    // use these lines on Mac version
                    string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "data");
                    path = Path.Combine(path, "test.pdf");
    
                    File.WriteAllBytes(path, bytes);
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
        }
    }
    

    enter image description here

    When I wrote this application, I've tested on Windows. So, if you're using Mac, you can replace the line:

    File.WriteAllBytes(model.OutputPath, bytes);
    

    in the file HtmlToPdf.cs to

    string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "data");
    path = Path.Combine(path, "test.pdf");
    
    File.WriteAllBytes(path, bytes);
    

    I've commented inside the code.

    About the font problem, if you want to use specific font (ex: Roboto), you must provide the font file and the path which your application can assign to.

    Nuget packages: iTextSharp and itextsharp.xmlworker

    You can convert this console application to web application, everytime you want to make PDF file, just make a request (ajax) to server and hit the method HtmlToPdf.Convert.