My Projects

🔍 Check out my latest project

Compare Word documents online with character-level precision

Visit DocumentDelta.com →

Friday, December 1, 2017

HTML to PDF without third party solutions



There are times when we need to convert a html page to pdf. There are a few options we can use to do this
  1. Use third Party solutions
  2. Creating a html render engine
  3. Using an existing html render engine like Firefox, chromium etc
Small projects or indie developers might need some simple and free way to provide the functionality to its customers.
Process
The problem statement is: We have html code which is rendered in a web browser and we need to convert that rendered page into pdf. The thinking process and workflow looks like this:
  1. Rendering the html code
As we only have the html code, we require a render engine to convert that code into readable page. Maybe we can use internet explorer’s engine which is used by the web browser control in a WinForms application. We can use different engine versions of internet explorer by placing specific html tags in head part.
  1. Taking a screen shot
We can take a screenshot of the rendered page and create an image of it. We can use the print screen functionality for this step. But print screen only takes what screen is showing and thus we would need to figure out how to take screenshot of the page even if its larger than the screen size.
  1. Putting the screen shot in a PDF
We can use itextsharp or similar libraries to create a pdf and put image file in it.
  1. All this in background
We need all this process in background to be executed from code without any human intervention
This way we can have pdf file of any rendered page from any html.
So here’s the entire code for the class which can do this process:
public static class Converter
   {
       public static Byte[] GetPdfFromHtml(string htmlfilePath)
       {
           if (!System.IO.File.Exists(htmlfilePath))
               return null;

           string imagefileName = System.IO.Path.GetTempFileName();
           string fileName = htmlfilePath;

           Thread pdfThread = new Thread(() => GenerateImage(fileName, imagefileName));
           pdfThread.SetApartmentState(ApartmentState.STA);
           pdfThread.Start();

           pdfThread.Join();

           var baseimage = new Bitmap(imagefileName);
           iTextSharp.text.Rectangle pageSize = new iTextSharp.text.Rectangle(baseimage.Width, baseimage.Height);
           baseimage.Dispose();

           string pdffileName = System.IO.Path.GetTempFileName();

           iTextSharp.text.Document document = new iTextSharp.text.Document(pageSize, 0, 0, 0, 0);

           using (var stream = new FileStream(pdffileName, FileMode.Create, FileAccess.Write, FileShare.None))
           {
               iTextSharp.text.pdf.PdfWriter.GetInstance(document, stream);
               document.Open();
               using (var imageStream = new FileStream(imagefileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
               {
                   var image = iTextSharp.text.Image.GetInstance(imageStream);
                   document.Add(image);
               }

               document.Close();
           }

           Byte[] bytes = System.IO.File.ReadAllBytes(pdffileName);
           System.IO.File.Delete(imagefileName);
           System.IO.File.Delete(pdffileName);
           return bytes;
       }

       private static Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
       {
           var destRect = new System.Drawing.Rectangle(0, 0, width, height);
           var destImage = new Bitmap(width, height);

           destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

           using (var graphics = Graphics.FromImage(destImage))
           {
               graphics.CompositingMode = CompositingMode.SourceCopy;
               graphics.CompositingQuality = CompositingQuality.HighQuality;
               graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
               graphics.SmoothingMode = SmoothingMode.HighQuality;
               graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

               using (var wrapMode = new System.Drawing.Imaging.ImageAttributes())
               {
                   wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                   graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
               }
           }

           return destImage;
       }

       private static void GenerateImage(string fileName, string imagefileName)
       {
           WebBrowser wb = new WebBrowser(); //WebBrowser control creation to render the page
           wb.ScrollBarsEnabled = false;
           wb.ScriptErrorsSuppressed = true;
           wb.Url = new Uri(fileName);

           while (wb.ReadyState != WebBrowserReadyState.Complete) //Wait to finish rendering
           {
               Application.DoEvents();
           }

           wb.Width = wb.Document.Body.ScrollRectangle.Width;
           wb.Height = wb.Document.Body.ScrollRectangle.Height;

           Bitmap screentshot = new Bitmap(wb.Width, wb.Height);
           GetImage(wb.ActiveXInstance, screentshot, Color.White); //Getting screen shot of the rendered page

           screentshot.Save(imagefileName);

           wb.Dispose();
       }

       [ComImport]
       [Guid("0000010D-0000-0000-C000-000000000046")]
       [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
       interface IViewObject
       {
           void Draw([MarshalAs(UnmanagedType.U4)] uint dwAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, IntPtr hdcTargetDev, IntPtr hdcDraw, [MarshalAs(UnmanagedType.Struct)] ref RECT lprcBounds, [In] IntPtr lprcWBounds, IntPtr pfnContinue, [MarshalAs(UnmanagedType.U4)] uint dwContinue);
       }

       [StructLayout(LayoutKind.Sequential, Pack = 4)]
       struct RECT
       {
           public int Left;
           public int Top;
           public int Right;
           public int Bottom;
       }

       private static void GetImage(object obj, System.Drawing.Image destination, Color backgroundColor)
       {
           using (Graphics graphics = Graphics.FromImage(destination))
           {
               IntPtr deviceContextHandle = IntPtr.Zero;
               RECT rectangle = new RECT();

               rectangle.Right = destination.Width;
               rectangle.Bottom = destination.Height;

               graphics.Clear(backgroundColor);

               try
               {
                   deviceContextHandle = graphics.GetHdc();

                   IViewObject viewObject = obj as IViewObject;
                   viewObject.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, deviceContextHandle, ref rectangle, IntPtr.Zero, IntPtr.Zero, 0);
               }
               finally
               {
                   if (deviceContextHandle != IntPtr.Zero)
                   {
                       graphics.ReleaseHdc(deviceContextHandle);
                   }
               }
           }
       }
   }

2 comments:

  1. Can we do something like converting *.pdf/*.doc/*.docx into HTML code without losing formatting that too without using any third party(paid) library.

    ReplyDelete
    Replies
    1. Hello,

      MS word provides a converter for doc and docx files and you can use its libraries to convert them.

      The only other option apart from paid libraries would be to write a custom parser to convert these files into html code. It can be done but it would need a lot of work.

      These files can be read by openXML and textSharp (both are free libs) and then would need to be passed into a custom library created for this task.

      Delete