2

Hi I'm trying to add cookies to the http headers when generating a pdf file with abcpdf. What I have done so far is based on the documentation:

http://www.websupergoo.com/helppdf9net/default.htm?page=source%2F5-abcpdf%2Fxhtmloptions%2F2-properties%2Fhttpadditionalheaders.htm

When requesting the page to be converted to PDF from my browser the headers are:

Connection: keep-alive
Accept: text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01
Accept-Encoding: gzip, deflate
Accept-Language: en-ie
Cookie: __utma=111872281.2030591713.1334325919.1348754241.1349943306.47; __utmz=111872281.1334325919.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); CurrentClient=AMF; UserHasMultipleClients=False; favFunds=%5B%7B%22id%22%3A57%2C%22alias_id%22%3A1690%7D%2C%7B%22id%22%3A794%2C%22alias_id%22%3Anull%7D%2C%7B%22id%22%3A818%2C%22alias_id%22%3Anull%7D%2C%7B%22id%22%3A21%2C%22alias_id%22%3Anull%7D%5D; favComparisons=%5B%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22794%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%2217%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%22593%22%2C%22alias%22%3A%22old%22%7D%2C%7B%22id%22%3A%22593%22%2C%22alias%22%3A%221606%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%2C%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22774%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%22215%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%2C%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22215%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%5D
Host: localhost:63706
Referer: http://localhost:63706/
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20100101 Firefox/15.0.1
X-Requested-With: XMLHttpRequest

When the request comes from abcpdf the headers by are:

Connection: keep-alive
Keep-Alive: 115
Accept: text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01
Accept-Charset: ISO-8859-1, utf-8; q=0.7, *; q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-us, en; q=0.5
Host: localhost:63706
Referer: http://localhost:63706/
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/ /2.0.1
X-Requested-With: XMLHttpRequest

As you can see it is missing the cookies. So I manually aded the following to the headers(theDoc.HtmlOptions.HttpAdditionalHeaders value before abcpdf send request)

Cookie: __utma=111872281.2030591713.1334325919.1348754241.1349943306.47; __utmz=111872281.1334325919.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); CurrentClient=AMF; UserHasMultipleClients=False; favFunds=%5B%7B%22id%22%3A57%2C%22alias_id%22%3A1690%7D%2C%7B%22id%22%3A794%2C%22alias_id%22%3Anull%7D%2C%7B%22id%22%3A818%2C%22alias_id%22%3Anull%7D%2C%7B%22id%22%3A21%2C%22alias_id%22%3Anull%7D%5D; favComparisons=%5B%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22794%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%2217%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%22593%22%2C%22alias%22%3A%22old%22%7D%2C%7B%22id%22%3A%22593%22%2C%22alias%22%3A%221606%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%2C%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22774%22%2C%22alias%22%3A%22null%22%7D%2C%7B%22id%22%3A%22215%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%2C%7B%22selectedFunds%22%3A%5B%7B%22id%22%3A%22215%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22selectedIndexes%22%3A%5B%7B%22id%22%3A%2224002259%22%2C%22alias%22%3A%22null%22%7D%5D%2C%22timePeriosSelection%22%3A%7B%22dateOne%22%3A%2223-11-2011%22%2C%22dateTwo%22%3A%2223-11-2012%22%7D%2C%22defaultTimePeriodSelection%22%3A%7B%22OneYear%22%3Atrue%2C%22ThreeYears%22%3Afalse%2C%22FiveYears%22%3Afalse%7D%7D%5D

This is the full code:

public MemoryStream HtmlToPdf(Uri pdfUrl, HttpCookieCollection cookies, bool landscape)
{
    var pdfStream = new MemoryStream();
    var theDoc = new Doc();
    if (landscape)
    {
        // apply a rotation transform
        double w = theDoc.MediaBox.Width;
        double h = theDoc.MediaBox.Height;
        double l = theDoc.MediaBox.Left;
        double b = theDoc.MediaBox.Bottom;
        theDoc.Transform.Rotate(90, l, b);
        theDoc.Transform.Translate(w, 0);
        // rotate our rectangle
        theDoc.Rect.Width = h;
        theDoc.Rect.Height = w;
    }

    theDoc.HtmlOptions.Engine = EngineType.Gecko;
    theDoc.HtmlOptions.Timeout = 60000;
    theDoc.HtmlOptions.UseScript = true;
    theDoc.HtmlOptions.OnLoadScript = "(function(){window.ABCpdf_go = false; setTimeout(function(){window.ABCpdf_go = true;}, 1000);})();";
    var url = Uri.UnescapeDataString(pdfUrl.ToString());

    if (cookies.Count > 0)
    {
        //send copy of cookies from original request
        var additionalHeadersString = "Cookie: ";
        foreach (var key in cookies.AllKeys)
        {
            if (!cookies.AllKeys[0].Equals(key)) //is NOT first element in cookie collection
            {
                additionalHeadersString += "; ";
            }
            additionalHeadersString += key + "=" + cookies[key].Value;
        }
        additionalHeadersString += "\r\n";
        theDoc.HtmlOptions.HttpAdditionalHeaders = additionalHeadersString;
        theDoc.HtmlOptions.NoCookie = true;
        theDoc.HtmlOptions.HostWebBrowser = false;
    }

    //send request
    var pageRef = theDoc.AddImageUrl(url, true, 1200, false);
    while (theDoc.Chainable(pageRef))
    {
        theDoc.Page = theDoc.AddPage();
        pageRef = theDoc.AddImageToChain(pageRef);
    }
    for (var i = 1; i <= theDoc.PageCount; i++)
    {
        theDoc.PageNumber = i;
        theDoc.Flatten();
    }

    theDoc.Save(pdfStream);
    theDoc.Clear();
    var byteInfo = pdfStream.ToArray();
    pdfStream.Write(byteInfo, 0, byteInfo.Length);
    pdfStream.Position = 0;
    return pdfStream;
}

The problem is that the cookies still missing from the header after using the HttpAdditionalHeaders property, do you know what I'm missing?

Thanks in advance :)

Remo H. Jansen
  • 23,172
  • 11
  • 70
  • 93

2 Answers2

1

Here the solution and why it didn't work:

Why it didn't work?

AbcPdf does NOT support theDoc.HtmlOptions.HttpAdditionalHeaders when using Gecko as rendering engine.

Work arround

Action that will generate PDF given an URL:

public class PdfController : Controller
{
    private readonly IPdfGenerator _pdfGenerator;

    public PdfController(IPdfGenerator pdfGenerator)
    {
        if (pdfGenerator == null)
            throw new ArgumentNullException();
        this._pdfGenerator = pdfGenerator;
    }

    public FileStreamResult Index(string url)
    {
        Response.AppendHeader("Content-Disposition", "attachment;filename=Download.Pdf");
        this._pdfGenerator.SetCookies(Request.Cookies);
        return new FileStreamResult(_pdfGenerator.HtmlToPdf(url.Uri, false, 1200), "application/pdf");
    }
}

PDF generation class:

    public class AbcPdfGenerator : IPdfGenerator
    {
        private static HttpCookieCollection _cookies = new HttpCookieCollection();

        public void SetCookies(HttpCookieCollection cookies)
        {
            lock (_cookies)
            {
                _cookies = cookies;
                System.Threading.Thread.MemoryBarrier();
            }
        }

        public HttpCookieCollection GetCookies()
        {
            return _cookies;
        }

        public MemoryStream HtmlToPdf(Uri pdfUrl, bool landscape, int screenResolutionWidth, bool enableCache)
        {
            var pdfStream = new MemoryStream();
            var theDoc = new Doc();
            if (landscape)
            {
                // apply a rotation transform
                double w = theDoc.MediaBox.Width;
                double h = theDoc.MediaBox.Height;
                double l = theDoc.MediaBox.Left;
                double b = theDoc.MediaBox.Bottom;
                theDoc.Transform.Rotate(90, l, b);
                theDoc.Transform.Translate(w, 0);
                // rotate our rectangle
                theDoc.Rect.Width = h;
                theDoc.Rect.Height = w;
            }
            theDoc.HtmlOptions.Engine = EngineType.Gecko;
            theDoc.HtmlOptions.Timeout = 60000;
            theDoc.HtmlOptions.UseScript = true;
            theDoc.HtmlOptions.OnLoadScript = "(function(){window.ABCpdf_go = false; setTimeout(function(){window.ABCpdf_go = true;}, 3000);})();";
            var url = Uri.UnescapeDataString(pdfUrl.ToString());

            var pageRef = theDoc.AddImageUrl(url, true, screenResolutionWidth, enableCache);
            while (theDoc.Chainable(pageRef))
            {
                theDoc.Page = theDoc.AddPage();
                pageRef = theDoc.AddImageToChain(pageRef);
            }
            for (var i = 1; i <= theDoc.PageCount; i++)
            {
                theDoc.PageNumber = i;
                theDoc.Flatten();
            }

            theDoc.Save(pdfStream);
            theDoc.Clear();
            var byteInfo = pdfStream.ToArray();
            pdfStream.Write(byteInfo, 0, byteInfo.Length);
            pdfStream.Position = 0;
            return pdfStream;
        }

        /// <summary>
        /// Will install the pdf software license on the server, this should be called on a Application_Start()
        /// </summary>
        public void InitializeService()
        {
            XSettings.InstallLicense(System.Configuration.ConfigurationManager.AppSettings["PdfGeneratorLicenseKey"]);
            //XSettings.InstallTrialLicense(System.Configuration.ConfigurationManager.AppSettings["PdfGeneratorLicenseKey"]);
        }
    }

Action that return HTML to be converted to PDF:

[RequireCookiesForPdf]
public ActionResult Index(string layout)
{
    return View();
}

The action filter used before:

public class RequireCookiesForPdf : ActionFilterAttribute
{
    private readonly IPdfGenerator _pdfGenerator;

    public RequireCookiesForPdf()
    {
        //"Bastard injection" :( as action filters don't allow constructor injection
        this._pdfGenerator = DependencyResolver.Current.GetService<IPdfGenerator>();
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var cookies = this._pdfGenerator.GetCookies();
        if (cookies != null)
        {
            foreach (var key in cookies.AllKeys)
            {
                var httpCookie = cookies[key];
                if (httpCookie != null) filterContext.HttpContext.Response.Cookies.Set(new HttpCookie(key, httpCookie.Value));
            }
            this._pdfGenerator.SetCookies(new HttpCookieCollection());
        }
        base.OnActionExecuting(filterContext);
    }
}
Remo H. Jansen
  • 23,172
  • 11
  • 70
  • 93
  • Not strictly true on the "Why doesnt it work" part. This page http://www.websupergoo.com/helppdfnet/source/5-abcpdf/xhtmloptions/2-properties/httpadditionalheaders.htm states that `Under Gecko, these headers are only sent if you are using the Screen Media or if you are using the POST RequestMethod` - UPDATE: Oops just seen that cocacho already said that (below) – Jimbo Apr 29 '17 at 10:26
1

AbcPdf does NOT support theDoc.HtmlOptions.HttpAdditionalHeaders when using Gecko as rendering engine....

Under Gecko, these headers are only sent if you are using the Screen Media or if you are using the POST RequestMethod. For instance:

theDoc.HtmlOptions.Media = MediaType.Screen;

Regards!

Andrei Zhytkevich
  • 8,039
  • 2
  • 31
  • 45
cocacho
  • 51
  • 6