Spire.PDF is a professional PDF library applied to creating, writing, editing, handling and reading PDF files without any external dependencies. Get free and professional technical support for Spire.PDF for .NET, Java, Android, C++, Python.

Mon May 11, 2020 9:50 pm

Hello, I am trying to generate a PDF file via C# code as part of a WebAPI.

I would like the page Header and Footer content (images, text, etc) to come from two separate html URLs, and the central content to come from another html URL.

Generally, it is structured like this:

---------------------------------------------------------------------------------------------
HEADER: IMG Title
---------------------------------------------------------------------------------------------
CONTENT: Text/Table
---------------------------------------------------------------------------------------------
FOOTER: Text
---------------------------------------------------------------------------------------------

Based on code samples from docs and some other forum posts, I have put together the following:

Code: Select all
      public StatusMessage CreatePdf(string SavePath, string ContentUrl)
        {
            var returnStatus = new StatusMessage();
             var mappedSavePath = Helpers.Files.GetMappedPath(SavePath);

            //create a PDF document
            PdfDocument doc = new PdfDocument();
            doc.PageSettings.Size = PdfPageSize.Letter;
            doc.PageSettings.Orientation = PdfPageOrientation.Landscape;

            //reset the default margins to 0
            doc.PageSettings.Margins = new PdfMargins(0);

            //Content Format settings
            PdfHtmlLayoutFormat htmlLayoutFormat = new PdfHtmlLayoutFormat();
            htmlLayoutFormat.FitToPage = Clip.None;
            htmlLayoutFormat.Layout = PdfLayoutType.Paginate;
            htmlLayoutFormat.IsWaiting = false;

            //All Templates and Content
            doc.PageSettings.SetMargins(SideMargins,HeaderHeight, SideMargins, FooterHeight);
            PdfMargins margins = doc.PageSettings.Margins;

            var topTemplate = CreateHeaderTemplate(doc);
            var bottomTemplate = CreateFooterTemplate(doc);

            var htmlContent = GetHtml(ContentUrl);
           
            Thread thread = new Thread(() =>
            {
                try
                {
                    //create a header template with content and apply it to page template
                    doc.Template.Top = topTemplate;

                    //create a footer template with content and apply it to bottom page template
                    doc.Template.Bottom = bottomTemplate;

                    //apply blank templates to other parts of page template
                    doc.Template.Left = new PdfPageTemplateElement(SideMargins, doc.PageSettings.Size.Height);
                    doc.Template.Right = new PdfPageTemplateElement(SideMargins, doc.PageSettings.Size.Height);

                    //Add Content
                    doc.LoadFromHTML(htmlContent, true, doc.PageSettings, htmlLayoutFormat);

                    //draw dynamic field in footer space
                    if (this.IncludePageNums)
                    {
                        doc = AddPageNums(doc);
                    }

                }
                catch (Exception e)
                {
                    returnStatus.Success = false;
                    returnStatus.Message = "An Error occurred which prevented PDF generation.";
                    returnStatus.RelatedException = e;
                }
            });
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();

            //Save pdf file.
            doc.SaveToFile(mappedSavePath);
            doc.Close();

            return returnStatus;
        }

private PdfPageTemplateElement CreateHeaderTemplate(PdfDocument doc)
        {
            //get page size
            PdfMargins margins = doc.PageSettings.Margins;
            SizeF pageSize = doc.PageSettings.Size;

            //create a PdfPageTemplateElement object as header space
            PdfPageTemplateElement headerSpace = new PdfPageTemplateElement(pageSize.Width, HeaderHeight);
            headerSpace.Foreground = true;

            //declare two float variables
            float leftPoint = margins.Left;
            float topPoint = 0;

            //RENDER
            var displayHtml =[CODE WHICH RETURNS HTML STRING]

            //Render HTML text
            PdfBrush brush = PdfBrushes.Black;
            PdfTrueTypeFont font = new PdfTrueTypeFont(_BrandFooterFont);
            PdfHTMLTextElement richTextElement = new PdfHTMLTextElement(displayHtml, font, brush);
            richTextElement.TextAlign = TextAlign.Left;
            PdfMetafileLayoutFormat format = new PdfMetafileLayoutFormat();
            format.Layout = PdfLayoutType.OnePage;
            format.Break = PdfLayoutBreakType.FitElement;
            //Draw htmlString
            PointF point = new PointF(leftPoint, topPoint);
            richTextElement.Draw(headerSpace.Graphics, point, headerSpace.Width, headerSpace.Height);

            return headerSpace;
        }

      private PdfPageTemplateElement CreateFooterTemplate(PdfDocument doc)
        {
            //get page size
            PdfMargins margins = doc.PageSettings.Margins;
            SizeF pageSize = doc.PageSettings.Size;

            //create a PdfPageTemplateElement object which works as footer space
            PdfPageTemplateElement footerSpace = new PdfPageTemplateElement(pageSize.Width, margins.Bottom);
            footerSpace.Foreground = true;

            //declare two float variables
            float leftPoint = margins.Left;
            float topPoint = pageSize.Height - FooterHeight ;

            //Get HTML
            var displayHtml =[CODE WHICH RETURNS HTML STRING]

            //Render HTML text
            PdfBrush brush = PdfBrushes.Black;
            PdfTrueTypeFont font = new PdfTrueTypeFont(_BrandFooterFont);
            PdfHTMLTextElement richTextElement = new PdfHTMLTextElement(displayHtml, font, brush);
            richTextElement.TextAlign = TextAlign.Left;
            PdfMetafileLayoutFormat format = new PdfMetafileLayoutFormat();
            format.Layout = PdfLayoutType.OnePage;
            format.Break = PdfLayoutBreakType.FitElement;
            //Draw htmlString
            PointF point = new PointF(leftPoint, topPoint);
            richTextElement.Draw(footerSpace.Graphics, point, footerSpace.Width, footerSpace.Height);

            return footerSpace;
        }

private PdfDocument AddPageNums(PdfDocument doc)
        {
            SizeF pageSize = doc.Pages[0].Size;
            float x = 90;
            float y = pageSize.Height - 72;

            for (int i = 0; i < doc.Pages.Count; i++)
            {
                //draw page number and page count at bottom
                PdfTrueTypeFont font = new PdfTrueTypeFont(_BrandFooterFont, true);
                PdfPageNumberField number = new PdfPageNumberField();
                PdfPageCountField count = new PdfPageCountField();
                PdfCompositeField compositeField = new PdfCompositeField(font, _BrandFooterColor, "Page {0} of {1}", number, count);
                compositeField.StringFormat = new PdfStringFormat(PdfTextAlignment.Right, PdfVerticalAlignment.Top);
                SizeF size = font.MeasureString(compositeField.Text);
                compositeField.Bounds = new RectangleF(pageSize.Width - x, y + 2, size.Width, size.Height);
                compositeField.Draw(doc.Pages[i].Canvas);
            }

            return doc;
        }


The Content is loaded into the PDF correctly, but the Header and Footer are just white areas. I also want to add the Page Number to all pages, in the bottom right, but that isn't rendering either.

Additionally, I am a bit confused about the Margins - it seems that the Page margins need to be large enough to accommodate the header/footer content, so setting the top and bottom margins only affects the main page content which is loaded in. But I am unclear about what the unit of the Margins is - is it pixels? or points? or something else?

Is there anything obviously wrong with my code?

NOTE: Using Acrobat 9.0 software, I see the following info:
Examine-PDF.png


"Show preview" on the hidden text shows the footer text and page numbers... I assume the "cropped" content would be the header... so how can I get these to be displayed?

hl_floyd
 
Posts: 5
Joined: Mon May 11, 2020 8:16 pm

Tue May 12, 2020 8:57 am

Hello,

Thanks for your inquiry.
Please refer to the attached code file, and I also attached my output file for your reference. If this doesn't meet your needs well, please provide your input html strings as well as your desired output. You could upload them here or send them to us([email protected]) via email.
Besides, the unit of the margin is point. If you have further questions, please feel free to let us know.

Sincerely,
Rachel
E-iceblue support team
User avatar

rachel.lei
 
Posts: 1571
Joined: Tue Jul 09, 2019 2:22 am

Tue May 12, 2020 5:07 pm

Thank you, Rachel,
Your code sample worked. I have a question about the header/footer HTML rendering, though.

This is the HTML for the footer (I have just replaced the real text content with sample content):

<div class="page-footer" style="height: 80px; margin-bottom: 2em;text-align:left;">
<p style="font-size: 11pt; margin-bottom: 0;margin-block-start:0;margin-block-end: 0;">Line 1</p>
<p style="font-size: 11pt;margin-bottom: 0;margin-block-start:0;margin-block-end: 0;">Line 2, a little longer</p>
<p style="font-size: 8pt; margin-top: 1em; margin-bottom: 2em;">Line 3 should be smaller and spaced out</p>
</div>

This is how the HTML renders in a browser:
HTML-display.png


But this is how it renders in the PDF footer:
PDF-display.png


The CSS font and spacing are not represented, and the first line is not aligned.

My header included an image, and an <HR> but that didn't get rendered either...
Is it possible to have the header/footer interpret the HTML and CSS correctly?

The HTML content was styled according to the CSS.

Additionally, I am finding that the RIGHT margin is way larger than the left, even if I set them with the same value.

Here is a screenshot of the whole PDF showing the weird margins (I put a border around the "content" so that I could see what width of content would fit - if I make the content any wider, it gets cut off on the right):
Demo.png


Also, even though I changed the code to load the Content URL directly, and set "enableHyperlinks" param to TRUE, the links aren't clickable.

Code: Select all
private static PdfDocument HtmlToPdf(string ContentUrl, SizeF PageSize, PdfMargins Margins)
        {
            PdfDocument doc = new PdfDocument();
            PdfPageSettings setting = new PdfPageSettings();
            //SizeF size = new SizeF(PageSize.Width - Margins.Left - Margins.Right, PageSize.Height - Margins.Top - Margins.Bottom);
            SizeF size = new SizeF(PageSize.Width, PageSize.Height - Margins.Top - Margins.Bottom);
            setting.Size = size;
            setting.Orientation = PdfPageOrientation.Landscape;
            setting.Margins = new Spire.Pdf.Graphics.PdfMargins(Margins.Left, 0, Margins.Right,0);

            PdfHtmlLayoutFormat htmlLayoutFormat = new PdfHtmlLayoutFormat();
            htmlLayoutFormat.FitToPage = Clip.None;
            htmlLayoutFormat.Layout = PdfLayoutType.Paginate;
            htmlLayoutFormat.IsWaiting = false;

             //string htmlContent = GetHtml(ContentUrl);
            //string htmlContent = "<p>test</p>";//My test data
            Thread thread = new Thread(() =>
            {
                //doc.LoadFromHTML(htmlContent, true, setting, htmlLayoutFormat);
                doc.LoadFromHTML(ContentUrl,false, true, true, setting, htmlLayoutFormat);
            });
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();
            return doc;
        }

hl_floyd
 
Posts: 5
Joined: Mon May 11, 2020 8:16 pm

Wed May 13, 2020 9:16 am

Hello,

Thanks for your feedback.
Regarding the HTML rendering issue, I learned from our Dev team that the PdfHTMLTextElement object doesn't actually support certain html elements or css attributes.
For your situation, I've recreated a demo for you. Please find the link below to download the sample project and test. In this project, I used the plugin method to convert HTML to PDF, which performs better than the normal method. And I have verified that the text will not be cut off and the links are clickable.
http://www.e-iceblue.com/downloads/demo/21533Demo.zip

Any questions, just feel free to contact us.

Sincerely,
Rachel
E-iceblue support team
User avatar

rachel.lei
 
Posts: 1571
Joined: Tue Jul 09, 2019 2:22 am

Wed May 13, 2020 2:46 pm

Thank you, Rachel,

Regarding :
the PdfHTMLTextElement object doesn't actually support certain html elements or css attributes.


"certain" is a bit vague... Does it support ANY CSS? Does it support IMG tags? Is there documentation about this somewhere?

hl_floyd
 
Posts: 5
Joined: Mon May 11, 2020 8:16 pm

Thu May 14, 2020 6:31 am

Hello,

Thanks for your response.
Sorry that the support of this method is not very good at present and there is no corresponding documentation about it. For your situation, I recommend you use the plugin method instead. For the specific code, please refer to the project I provided above, which demonstrates how to render the HTML containing the <img> tag and the <hr> tag.

Sincerely,
Rachel
E-iceblue support team
User avatar

rachel.lei
 
Posts: 1571
Joined: Tue Jul 09, 2019 2:22 am

Wed Jun 10, 2020 4:44 am

Thank you!

Yes, the plugin works much better - CSS, links and images (with absolute urls) all came through.

hl_floyd
 
Posts: 5
Joined: Mon May 11, 2020 8:16 pm

Wed Jun 10, 2020 6:14 am

Hello,

Thanks for your feedback.
If you have further questions, please feel free to let us know.
Have a nice day!

Sincerely,
Rachel
E-iceblue support team
User avatar

rachel.lei
 
Posts: 1571
Joined: Tue Jul 09, 2019 2:22 am

Wed Jun 10, 2020 3:52 pm

Just one question - Is there any way to set "page break" markers in the main Content HTML which could be "read" when it is being converted, and cause a page break to be added at that point?

hl_floyd
 
Posts: 5
Joined: Mon May 11, 2020 8:16 pm

Thu Jun 11, 2020 7:00 am

Hello,

Thanks for your post.
Our plugin method supports the css styles "page-break-after: always;" and "page-break-before: always;", please refer to the following html text. And attached is my output file for your reference. Feel free to contact us if you have further questions.
Code: Select all
      <p style="page-break-after: always;">This is first page.</p>
      <p>This is second page.</p>
      <p style="page-break-before: always;">This is last page.</p>


Sincerely,
Rachel
E-iceblue support team
User avatar

rachel.lei
 
Posts: 1571
Joined: Tue Jul 09, 2019 2:22 am

Return to Spire.PDF

cron