0

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

AngryOtter
  • 149
  • 9
  • @KJ Nope, I even hardcoded the absolutely path to the image, still didn't work – AngryOtter Sep 16 '21 at 19:28
  • @KJ gif is still not there even if I set the url to `"./img/"` instead. Absolutely path is not desired because prod environment will have a different path. But even that did not work either when I do `properties.SetBaseUri("C:/Users/User Name/Desktop/TrackWSSample/img");` – AngryOtter Sep 17 '21 at 12:59
  • Can you add the code that sends/receives the request so that I can test? Obviously, replace your username, password, and AccessLicenseNumber with generic data. (ex: my_username, my_password, my_access_license_number). – Tu deschizi eu inchid Sep 17 '21 at 13:39
  • @user9938 added, it is not much different from the sample code that UPS provided. – AngryOtter Sep 17 '21 at 13:45

1 Answers1

5

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:

Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24
  • Thank you very much for detailed write up. Unfortunately, I tried with both absolute and relatively path using ConverterProperty and it didn't work still. The gif is still missing. I have uploaded the html that I used as well as file structure. – AngryOtter Sep 16 '21 at 19:21
  • `If you use a String parameter to pass your HTML, then the default value will be the folder where the code is executed. `, Without using ConverterProperty, it seems like that it will still look for the resource file in the current directory, then I don't know why my original approach did not work either. – AngryOtter Sep 16 '21 at 19:23
  • @AngryOtter: The above code was tested using an HTML file with a .gif file. Try creating the following folder: `C:\Temp` and place both the HTML file and image file in `C:\Temp` – Tu deschizi eu inchid Sep 16 '21 at 19:38
  • I obtained this HTML string as part of the SOAP response from the UPS API , so it is not going to create the file. Rather it needs to convert into PDF directly from the string. – AngryOtter Sep 16 '21 at 19:50
  • @AngryOtter: Looks like UPS has a number of APIs. Which one are you using? – Tu deschizi eu inchid Sep 16 '21 at 20:36
  • Signature Proof of Delivery API. I don't think this matter. The HTML Base64 encoded string is the only format they provide. So I decoded it and got above HTML string, which I needed to converted it to PDF. – AngryOtter Sep 16 '21 at 20:38
  • It appears that the logo image (.gif) can be found in the "Signature Tracking.zip" file, under "Common and General\Logos". Copy this file to your desired location prior to converting to PDF so that it's available during the conversion process. – Tu deschizi eu inchid Sep 16 '21 at 21:55
  • I do have the logo ready. That's how I was able to see the image if I just directly open the HTML with browser, but not when it is converted to PDF. Please see my edited answer where I put my file structure – AngryOtter Sep 17 '21 at 12:57
  • For sanity check, I have copied past your code and put the ups_logo.gif in the same file of the HTML file. Same result. Gif is not showing. – AngryOtter Sep 17 '21 at 13:11
  • Try the code that I added under "Update 2" to see if it works for you. – Tu deschizi eu inchid Sep 17 '21 at 16:18
  • what is the namespace for ImageSizeType? it doesn't seem to be in System.Drawing or Sytem.Drawing.Imaging – AngryOtter Sep 17 '21 at 18:18
  • It's an enum, I've added it to the post. When resizing the logo image, we want to maintain the aspect ratio for the image, so we only want to allow either the width to be specified or the height-not both. Whichever value wasn't specified will be calculated. I've used `ImageSizeType` along with `newSizeWidthOrHeight` to specify whether the specified size parameter is for the width or for the height. – Tu deschizi eu inchid Sep 17 '21 at 19:39
  • thanks a lot of the effort you put into this answer. It still didn't work with update 2 and I have decided to move on and try other library. Itext7 seems to be way to inconvenient for a PAID library. I will mark it as an answer tho. – AngryOtter Sep 20 '21 at 13:44