Search code examples
c#itextpdf-generationitext7

Itext7 HtmlConverter does not display gif


I am trying convert the html to pdf that has a image tag with relative path.

<IMG src="./ups_logo.gif" ALT="UPS Logo">

This is an dynamic HTML I got from the API.

when I convert it to PDF using HTMLConverter like so:

string htmlString = "<!DOCTYPE HTML PUBLIC \" -//W3C//DTD HTML 4.0 Transitional//EN\"><HTML><HEAD><META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\"><TITLE>UPS Package Tracking</TITLE></HEAD><BODY><TABLE width=500 border=0 cellSpacing=0 cellPadding=0><TR><TD rowspan=\"30\" Width=\"17\">&nbsp;</TD><TD><TABLE cellSpacing=0 cellPadding=0 border=0 ><TR><TD align=\"left\" width=\"30\" > <IMG src=\"./ups_logo.gif\" ALT=\"UPS Logo\"></TD><TD><IMG src=\"./ups_banner.gif\" ALT=\"UPS Banner\" ></TD></TR></TABLE></TD></TR><TR><TD><TABLE cellSpacing=0 cellPadding=0 width=500 border=0 ><TR><TD colSpan=2 align=left><B>DELIVERY NOTIFICATION</B></TD><TD></TD></TR><TR><TD>&nbsp;</TD></TR><TR><TD colSpan=2>Dear Customer,</TD></TR><TR><TD>&nbsp;</TD></TR><TR><TD colSpan=3>This is in response to your request for delivery information concerning the shipment listed below.</TD></TR><TR><TD>&nbsp;</TD></TR><TR><TD align=right>Tracking Number:&nbsp;&nbsp;</TD><TD align=left>XX XXX XXX XX XXXX XXX X</TD></TR><TR><TD valign=top align=right><nobr>&nbsp;&nbsp;&nbsp;Reference Number(s):&nbsp;&nbsp;</nobr></TD><TD align=left><Table cellSpacing=0 cellPadding=0 width = 65%><TR><TD>XXXXXXXXXXXXXXXXX, XXXX, XXXXXXXXXXXXXXXXX, XXXXX5  </TD></TR></TABLE></TD></TR><TR><TD align=right>Service Type:&nbsp;&nbsp;</TD><TD align=left>UPS GROUND</TD></TR><TR><TD align=right>Package Weight:&nbsp;&nbsp;</TD><TD align=left>7.00 LBS</TD></TR><TR><TD align=right>Shipped or Billed on:&nbsp;&nbsp;</TD><TD align=left>Sep 01, 2021</TD></TR><TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR><TR><TD align=right valign=top>Delivered on:&nbsp;&nbsp;</TD><TD align=left>Sep 07, 2021 3:10 P.M.</TD></TR><TR><TD align=right valign=top>Delivered to:&nbsp;&nbsp;</TD><TD align=left>York, NY, US, 11111</TD></TR><TR><TD align=right>Signed By:&nbsp;&nbsp;</TD><TD align=left>AngryOtter</TD></TR><TR><TD align=right>Location:&nbsp;&nbsp;</TD><TD align=left>Receiver</TD></TR><TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR><TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>  <TR><TD colSpan=2>Thank you for giving us this opportunity to serve you.</TD></TR><TR><TD>&nbsp;</TD></TR>  <TR><TD>Sincerely,</TD></TR>  <TR><TD>United Parcel Service</TD></TR><TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR><TR><TD colSpan=2>Tracking results provided by UPS: &nbsp;Sep 16, 2021 2:02 P.M. Eastern Time (USA)</TD></TR></TABLE></TD></TR></TABLE></BODY></HTML>";

        byte[] pdfData;
        using (var memoryStream = new MemoryStream())
        {
                ConverterProperties properties = new ConverterProperties();
                properties.SetBaseUri("../img/");
                HtmlConverter.ConvertToPdf(htmlString, memoryStream,properties);
                pdfData = memoryStream.ToArray();
        }
        File.WriteAllBytes("c:\\test.pdf", pdfData);

Everything else is displaying great, I got the PDF to generate and all the information are there, but the image is not displaying. It is not broken either, it is just not there. There is no exception either. The source file of the image is on the solution level of the project currently.

File Structure:

Desktop
  --TrackWSSample
    --.sln
    --.suo
    --img(folder)
      --ups_logo.gif
    --TrackWSSample
      --app.config
      --package.config
      --program.cs

Edit: My request/response code:

        TrackService track = new TrackService();
        TrackRequest tr = new TrackRequest();
        UPSSecurity upss = new UPSSecurity();

        UPSSecurityServiceAccessToken upssSvcAccessToken = new UPSSecurityServiceAccessToken();
        upssSvcAccessToken.AccessLicenseNumber = "my_number";
        upss.ServiceAccessToken = upssSvcAccessToken;

        UPSSecurityUsernameToken upssUsrNameToken = new UPSSecurityUsernameToken();
        upssUsrNameToken.Username = "userName";
        upssUsrNameToken.Password = "Password";
        upss.UsernameToken = upssUsrNameToken;

        track.UPSSecurityValue = upss;
        RequestType request = new RequestType();
        string[] requestOption = { "14" };
        request.RequestOption = requestOption;
        tr.Request = request;
        tr.InquiryNumber = "my_tracking_number";
        System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11; //This line will ensure the latest security protocol for consuming the web service call.
        TrackResponse trackResponse = track.ProcessTrack(tr);
        string s = trackResponse.Shipment[0].Package[0].Activity[0].Document[0].Content;
        byte[] htmlBytes = Convert.FromBase64String(s);
        string htmlString = Encoding.UTF8.GetString(htmlBytes);

Any help is appreciated


Solution

  • According to pdfHTML: configuration options:

    ConverterProperties: Through the various method overloads, you can specify certain input parameter types in the first two arguments, but there is always the optional third parameter ConverterProperties. This parameter contains the basic configuration options that allow users to customize handling of the input data in various ways. We will now elaborate on these options so that you can configure your pdfHTML code for optimal results.

    baseUri: If the HTML file requires any external resources, such as a standalone CSS file or image files, then pdfHTML file needs to know where these are located. That location may either be a URI on the local file system or online.

    pdfHTML will try to use a reasonable default value in case you don't specify a baseUri. If you use a String parameter to pass your HTML, then the default value will be the folder where the code is executed. If you use the overload of convertToPdf with a File parameter, it will use the same location as the input file.

    If neither of these defaults is right for you, then you will need to define the default resource location root. Then, any references in the HTML file will start from that path to find the resource. It is possible to use the ../ syntax to go up in the directory tree of your file system, but for other purposes, the specified URI acts as the root folder for determining paths.

    Pre-requisites:

    • Download/install NuGet package itext7
    • Download/install NuGet package itext7.pdfhtml

    Add the following using statements:

    using System;
    using System.IO;
    using iText.Html2pdf;
    

    Try the following:

    Note: Specify HTML filename, PDF filename (to save as), and the baseUri (if the HTML file contains resources such as images or stylesheets)

    CreatePdf

    public static void CreatePdf(string htmlFilename, string pdfFilename, string baseUri = "")
    {
        byte[] pdfData;
    
        if (!System.IO.File.Exists(htmlFilename))
        {
            throw new Exception("Error: '" + htmlFilename + "' doesn't exist.");
        }
    
        using (FileStream fs = new FileStream(htmlFilename, FileMode.Open, FileAccess.Read))
        {
            using (var memoryStream = new MemoryStream())
            {
                //when specifying HTML as a string and the HTML includes
                //a resource that uses relative paths,
                //it's necessary to specify the baseUri (path)
    
                //create new instance
                ConverterProperties properties = new ConverterProperties();
    
                if (!String.IsNullOrEmpty(baseUri))
                {
                    //set value
                    properties.SetBaseUri(baseUri);
                }
                else
                {
                    //get folder name that HTML file exists in
                    string folderName = System.IO.Path.GetDirectoryName(htmlFilename);
    
                    //set value
                    properties.SetBaseUri(folderName);
                }
    
                //System.Diagnostics.Debug.WriteLine("BaseURI: " + properties.GetBaseUri());
    
                //convert HTML to PDF
                HtmlConverter.ConvertToPdf(fs, memoryStream, properties);
    
                //save to byte[]
                pdfData = memoryStream.ToArray();
            }
        }
    
        File.WriteAllBytes(pdfFilename, pdfData);
    }
    
    public static void CreatePdfFromHtmlString(string htmlString, string pdfFilename, string baseUri)
    {
    
        byte[] pdfData;
        using (var memoryStream = new MemoryStream())
        {
            //when specifying HTML as a string and the HTML includes
            //a resource that uses relative paths,
            //it's necessary to specify the baseUri (path)
    
            //create new instance
            ConverterProperties properties = new ConverterProperties();
    
            //set value
            properties.SetBaseUri(baseUri);
    
            //convert HTML to PDF
            HtmlConverter.ConvertToPdf(htmlString, memoryStream, properties);
    
            //save to byte[]
            pdfData = memoryStream.ToArray();
        }
    
        File.WriteAllBytes(pdfFilename, pdfData);
    }
    

    Usage: Specify HTML as string

    string htmlFilename = @"C:\Temp\default.html";
    string folderName = System.IO.Path.GetDirectoryName(htmlFilename);
    string html = System.IO.File.ReadAllText(htmlFilename);
    string pdfFilename = @"C:\Temp\default.pdf";
    
    //convert HTML to PDF and save to file
    CreatePdfFromHtmlString(html, pdfFilename, folderName);
    

    Usage: Specify HTML filename

    string htmlFilename = @"C:\Temp\default.html";
    string pdfFilename = @"C:\Temp\default.pdf";
    
    //convert HTML to PDF and save to file
    CreatePdf(htmlFilename, pdfFilename);
    

    Update:

    Below is the HTML file that was used for testing:

    default.html:

    <!DOCTYPE html>
    
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
        <head>
            <meta charset="utf-8" />
            <title></title>
        </head>
        <body>
            <h2>HTML Test</h2>
    
            <div>
                <IMG src="./ups_logo.gif" ALT="UPS Logo">
            </div>
    
            <p>
                <div>
                    This is a test message
                </div>
        </body>
    </html>
    

    Note: An image file named 'ups_logo.gif' should be in the same folder as the HTML file.


    Update 2:

    Since you seem to be having issues getting the logo image from file, convert the logo image inside the .zip file downloaded from UPS Developer Kit to a base64 string using the following method - you'll find the logo image in "Common and General\Logos\LOGO_L.gif" inside the .zip file.

    ConvertImageToBase64String

    private static string ConvertImageToBase64String(string filename)
    {
        string result = string.Empty;
    
        using (System.Drawing.Image image = System.Drawing.Image.FromFile(filename))
        {
            using (System.IO.MemoryStream m = new System.IO.MemoryStream())
            {
                image.Save(m, image.RawFormat);
                byte[] imageBytes = m.ToArray();
    
                //convert byte[] to base64 string
                result = Convert.ToBase64String(imageBytes);
            }
        }
    
        return result;
    }
    

    Note: This only needs to be done once. Unfortunately, the base64 string is too long to be able to post it here. In the code below, replace <logo image as base64 string> with the base64 string for the logo image.

    If not already added, add the following using statements:

    using System;
    using System.IO;
    using iText.Html2pdf;
    

    ImageSizeType

    public enum ImageSizeType
    {
        Width,
        Height
    }
    

    Add the following methods to your code:

    public static byte[] GetUpsLogo(int newSizeWidthOrHeight, ImageSizeType sizeType)
    {
        byte[] imageBytes = null;
    
         //ToDo: add logo image as base64 string
        string upsLogoLBase64Str = <logo image as base64 string>;
    
        //convert base64 string to byte[]
        imageBytes = Convert.FromBase64String(upsLogoLBase64Str);
    
        //resize image
        imageBytes = ResizeImageMaintainingAspectRatio(imageBytes, newSizeWidthOrHeight, sizeType);
    
        return imageBytes;
    }
    
    public static string GetUpsLogoAsBase64String(int newSizeWidthOrHeight, ImageSizeType sizeType)
    {
        string upsLogoBase64Str = string.Empty;
    
        //get UPS logo
        byte[] imageBytes = GetUpsLogo(newSizeWidthOrHeight, sizeType);
    
        //convert to base64 string
        upsLogoBase64Str = Convert.ToBase64String(imageBytes);
    
        return upsLogoBase64Str;
    }
    
    public static byte[] ResizeImageMaintainingAspectRatio(byte[] imageBytes, int newSizeWidthOrHeight, ImageSizeType sizeType)
    {
        byte[] result = null;
    
        //resize image
        using (MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length))
        {
            using (System.Drawing.Image img = System.Drawing.Image.FromStream(ms))
            {
                int sourceWidth = img.Width;
                int sourceHeight = img.Height;
                int newWidth = 0;
                int newHeight = 0;
    
                if (sizeType == ImageSizeType.Width)
                {
                    //set value
                    newWidth = newSizeWidthOrHeight;
    
                    //calculate new height
                    newHeight = newSizeWidthOrHeight * sourceHeight / sourceWidth;
    
                }
                else
                {
                    //set value
                    newHeight = newSizeWidthOrHeight;
    
                    //calculate new width
                    newWidth = newSizeWidthOrHeight * sourceWidth / sourceHeight;
                }
    
                using (System.Drawing.Bitmap b = new System.Drawing.Bitmap(img, new System.Drawing.Size(newWidth, newHeight)))
                {
                    using (MemoryStream ms2 = new MemoryStream())
                    {
                        //b.Save(ms2, System.Drawing.Imaging.ImageFormat.Jpeg);
                        b.Save(ms2, System.Drawing.Imaging.ImageFormat.Gif);
                        result = ms2.ToArray();
                    }
                }
            }
        }
    
        return result;
    }
    
    public static void SaveUpsLogo(string filename, int newSizeWidthOrHeight, ImageSizeType sizeType)
    {
        //get UPS logo
        byte[] imageBytes = GetUpsLogo(newSizeWidthOrHeight, sizeType);
    
        //save to file
        System.IO.File.WriteAllBytes(filename, imageBytes);
    }
    

    If using the code posted in "Update 2", the following code can be used for CreatePdfFromHtmlString, since the HTML will no longer contain a reference for the logo image. I've left the original method above, as it may be useful for others who come across this post.

    CreatePdfFromHtmlString:

    public static void CreatePdfFromHtmlString(string htmlString, string pdfFilename)
    {
        byte[] pdfData;
    
        using (var memoryStream = new MemoryStream())
        {
            //when specifying HTML as a string and the HTML includes
            //a resource that uses relative paths,
            //it's necessary to specify the baseUri (path) 
            //use the overload that allows baseUri to be specified
    
            //convert HTML to PDF
            HtmlConverter.ConvertToPdf(htmlString, memoryStream);
    
            //save to byte[]
            pdfData = memoryStream.ToArray();
        }
    
        File.WriteAllBytes(pdfFilename, pdfData);
    }
    

    Usage:

    string html = "<!DOCTYPE HTML PUBLIC \" -//W3C//DTD HTML 4.0 Transitional//EN\"><HTML><HEAD><META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\"><TITLE>UPS Package Tracking</TITLE></HEAD><BODY><TABLE width=500 border=0 cellSpacing=0 cellPadding=0><TR><TD rowspan=\"30\" Width=\"17\">&nbsp;</TD><TD><TABLE cellSpacing=0 cellPadding=0 border=0 ><TR><TD align=\"left\" width=\"30\" > <IMG src=\"./ups_logo.gif\" ALT=\"UPS Logo\"></TD><TD><IMG src=\"./ups_banner.gif\" ALT=\"UPS Banner\" ></TD></TR></TABLE></TD></TR><TR><TD><TABLE cellSpacing=0 cellPadding=0 width=500 border=0 ><TR><TD colSpan=2 align=left><B>DELIVERY NOTIFICATION</B></TD><TD></TD></TR><TR><TD>&nbsp;</TD></TR><TR><TD colSpan=2>Dear Customer,</TD></TR><TR><TD>&nbsp;</TD></TR><TR><TD colSpan=3>This is in response to your request for delivery information concerning the shipment listed below.</TD></TR><TR><TD>&nbsp;</TD></TR><TR><TD align=right>Tracking Number:&nbsp;&nbsp;</TD><TD align=left>XX XXX XXX XX XXXX XXX X</TD></TR><TR><TD valign=top align=right><nobr>&nbsp;&nbsp;&nbsp;Reference Number(s):&nbsp;&nbsp;</nobr></TD><TD align=left><Table cellSpacing=0 cellPadding=0 width = 65%><TR><TD>XXXXXXXXXXXXXXXXX, XXXX, XXXXXXXXXXXXXXXXX, XXXXX5  </TD></TR></TABLE></TD></TR><TR><TD align=right>Service Type:&nbsp;&nbsp;</TD><TD align=left>UPS GROUND</TD></TR><TR><TD align=right>Package Weight:&nbsp;&nbsp;</TD><TD align=left>7.00 LBS</TD></TR><TR><TD align=right>Shipped or Billed on:&nbsp;&nbsp;</TD><TD align=left>Sep 01, 2021</TD></TR><TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR><TR><TD align=right valign=top>Delivered on:&nbsp;&nbsp;</TD><TD align=left>Sep 07, 2021 3:10 P.M.</TD></TR><TR><TD align=right valign=top>Delivered to:&nbsp;&nbsp;</TD><TD align=left>York, NY, US, 11111</TD></TR><TR><TD align=right>Signed By:&nbsp;&nbsp;</TD><TD align=left>AngryOtter</TD></TR><TR><TD align=right>Location:&nbsp;&nbsp;</TD><TD align=left>Receiver</TD></TR><TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR><TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>  <TR><TD colSpan=2>Thank you for giving us this opportunity to serve you.</TD></TR><TR><TD>&nbsp;</TD></TR>  <TR><TD>Sincerely,</TD></TR>  <TR><TD>United Parcel Service</TD></TR><TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR><TR><TD colSpan=2>Tracking results provided by UPS: &nbsp;Sep 16, 2021 2:02 P.M. Eastern Time (USA)</TD></TR></TABLE></TD></TR></TABLE></BODY></HTML>";
    
    string upsLogoBase64Str = GetUpsLogoAsBase64String(100, ImageSizeType.Height);
    string base64SrcStr = "data:image/gif;base64, " + upsLogoBase64Str;
    html = html.Replace("./ups_logo.gif", base64SrcStr);
    
    SaveFileDialog sfd = new SaveFileDialog();
    sfd.Filter = "PDF File (*.pdf)|*.pdf";
    
    //prompt for filename to save PDF file as
    if (sfd.ShowDialog() == DialogResult.OK)
    {
        CreatePdfFromHtmlString(html, sfd.FileName);
    }
    

    Resources: