Edit Total Number of Pages in Footer SelectPDF - c#

I am converting html page to pdf using HtmlToPdf() of SelectPDF. Since html content is big, I am breaking it in half and creating 2 PDFs.
I am struggling to edit the total_pages in the footer to display actual total number of the pages, not only the current document; as well as page_number to display the actual page number in the context of both PDFs.
How can I assess {page_number} and {total_pages} to calculate proper values? All examples I found use PdfDocument(), not HtmlToPdf().
Dim converter As New HtmlToPdf()
Dim text As New PdfTextSection(0, 10, "Page: {page_number} of {total_pages} ")
text.HorizontalAlign = PdfTextHorizontalAlign.Center
converter.Footer.Add(text)
I am tagging both C# and VB since SelectPDF is for both languages, and relevant sample from either one will work for me. Thank you

Today I've stumbled upon the same issue and I have found a work-around for the problem. The converter was able to show page numbers for it's the generated document but can't be aware of multiple generated files (you can't access the page properties) so all my pages I concatenated were showing Page 1 of 1.
First I define one PdfDocument (see it as the main document) and I use HtmlToPdf to append html converted files to this main document.
// Create converter
converter = new HtmlToPdf();
PdfTextSection text = new PdfTextSection(0, 10, "Page: {page_number} of {total_pages} ", new Font("Arial", 8));
text.HorizontalAlign = PdfTextHorizontalAlign.Right;
converter.Footer.Add(text);
// Create main document
pdfDocument = new PdfDocument();
Then I add pages (from html) using this method
public void AddPage(string htmlPage)
{
PdfDocument doc = converter.ConvertHtmlString(htmlPage);
pdfDocument.Append(doc);
converter.Footer.TotalPagesOffset += doc.Pages.Count;
converter.Footer.FirstPageNumber += doc.Pages.Count;
}
This results in correct page numbers for the main document. The same trick could be used for splitting files and page numbers over multiple documents like you described.
EDIT: In case you don't see any page numbering using the HtmlToPdf converter, don't forget to set following property:
converter.Options.DisplayFooter = true;

There is an open source library called itextsharp that will help get total page count.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTextSharp.text.pdf;
using iTextSharp.text.xml;
namespace GetPages_PDF
{
class Program
{
static void Main(string[] args)
{
// Right side of equation is location of YOUR pdf file
string ppath = "C:\\aworking\\Hawkins.pdf";
PdfReader pdfReader = new PdfReader(ppath);
int numberOfPages = pdfReader.NumberOfPages;
Console.WriteLine(numberOfPages);
Console.ReadLine();
}
}
}
Then you can stamp text also on the page but you will need to add the location to where it needs to go.
link: http://crmhunt.com/how-to-modify-pdf-file-using-itextsharp/
hope this helps in some way.

You should use the following properties:
FirstPageNumber - Controls the page number for the first page being
rendered.
TotalPagesOffset - Controls the total number of pages
offset in the generated pdf document.
More details here:
http://selectpdf.com/html-to-pdf/docs/html/HtmlToPdfHeadersAndFooters.htm

The answers above did not work for me as I was trying to merge multiple PDFs with different orientations. bonnoj's answer did add page numbers but they were incorrect and I couldn't find a way to correct them. So I took a different approach - I created a PDF, then for each HTML page I added a pdfPage and then added a PdfHtmlElement to that page. Finally I loop over the pages and add a custom footer to each page. This may not be the most efficient way to do this but it's the only way that I could find that added the footer in the correct place when mixing portrait and landscape pages. Hopefully it will save somebody else spending hours playing with different properties.
var pdfDocument = new PdfDocument(PdfStandard.Full);
foreach (var (html, pdfPageOrientation) in pages)
{
var page = pdfDocument.AddPage(PdfCustomPageSize.A4, new PdfMargins(marginLeft, marginRight, marginTop, marginBottom));
page.Orientation = pdfPageOrientation;
var pdfHtmlElement = new PdfHtmlElement(html, "");
page.Add(pdfHtmlElement);
}
var pdfFont = pdfDocument.AddFont(PdfStandardFont.Helvetica);
pdfFont.Size = 12;
foreach (PdfPage page in pdfDocument.Pages)
{
var customFooter = pdfDocument.AddTemplate(page.PageSize.Width, 30);
var pdfFooterTextElement = new PdfTextElement(0, 15,
pageFooterText,
pdfFont)
{
HorizontalAlign = PdfTextHorizontalAlign.Right,
VerticalAlign = PdfTextVerticalAlign.Bottom,
};
customFooter.Add(pdfFooterTextElement);
page.CustomFooter = customFooter;
}
pdfDocument.Save(stream);

Related

How to dynamically link bookmarks to table of contents using PDFsharp + MigraDoc

I'm trying to create a Table of Contents using MigraDoc and PDFsharp and I've gotten really close but the problem I'm currently having is that the links on the Table of Contents all take me to the very first page of the PDF. I'm trying to link them to their respective pages. PDFSharp bookmarks work fine but when trying to create a table of contents based on the merged PDF it's not working.
static void TableOfContents(PdfDocument document)
{
// Puts the Table of contents on the second page
PdfPage page = document.Pages[1];
XGraphics gfx = XGraphics.FromPdfPage(page);
gfx.MUH = PdfFontEncoding.Unicode;
// Create MigraDoc document + Setup styles
Document doc = new Document();
Styles.DefineStyles(doc);
// Add header
Section section = doc.AddSection();
Paragraph paragraph = section.AddParagraph("Table of Contents");
paragraph.Format.Font.Size = 14;
paragraph.Format.Font.Bold = true;
paragraph.Format.SpaceAfter = 24;
paragraph.Format.OutlineLevel = OutlineLevel.Level1;
// Add links - these are the PdfSharp outlines/bookmarks
// added previously when concatinating the pages
foreach (var bookmark in document.Outlines)
{
paragraph = section.AddParagraph();
paragraph.Style = "TOC";
paragraph.AddBookmark(bookmark.Title);
Hyperlink hyperlink = paragraph.AddHyperlink(bookmark.Title);
hyperlink.AddText($"{bookmark.Title}\t");
hyperlink.AddPageRefField(bookmark.Title);
}
// Render document
DocumentRenderer docRenderer = new DocumentRenderer(doc);
docRenderer.PrepareDocument();
docRenderer.RenderPage(gfx, 1);
gfx.Dispose();
}
Ideally I want it to return the file's name (which it's doing) and the page number (it's only returning the first page). This is what it's currently outputting.
Table of Contents
file name here......................... 1
file name here......................... 1
file name here......................... 1
file name here......................... 1
As I understand it, the Hyperlink and bookmark should be unique to the document.
Otherwise the link will be made to the first paragraph containing the bookmark.
I simply use a number which I increase for a simple report I make.
private void DefineTOCLine(int level, string text, Paragraph linkTo)
{
var tocIndex = (tocindex++).ToString(CultureInfo.InvariantCulture);
var paragraph = tocsection.AddParagraph();
paragraph.Style = level == 1 ? "TOC1" : "TOC2";
var hyperlink = paragraph.AddHyperlink(tocIndex);
hyperlink.AddText(text + "\t");
hyperlink.AddPageRefField(tocIndex);
linkTo.AddBookmark(tocIndex);
}
You invoke hyperlink.AddPageRefField to set a reference, but as far as I can tell you never create the MigraDoc bookmark for the target of the reference by calling MigraDoc's AddBookmark method.
MigraDoc bookmarks are different from PDF file bookmarks.

Create Multi-page Index File(TOC) for merged pdf using itext library in java

How can I write a multi-page ToC to the end of a PDF consisting of merged documents, using iTextSharp?
The answer to Create Index File(TOC) for merged pdf using itext library in java explains how to create a ToC page when merging PDFs (catalogued in the iTextSharp book http://developers.itextpdf.com/examples/merging-pdf-documents/merging-documents-and-create-table-contents#795-mergewithtoc.java). Code in this answer is based on those examples.
However it only works if the ToC is 1 page long. If the content becomes longer, then it repeats itself on the same page rather than spanning into the next page.
Trying to add the link directly to the text via:
ct.Add(new Chunk("link").SetLocalGoto("p1"))
causes an exception ("Cannot add Annotations, not enough pages in document").
Can anyone explain a method that will allow me to append multiple pages of content to a PDF when merging them (the more general the approach, the better). Is there a way to write into the document using Document.Add() instead of having to copy in template pages and write on the top of them?
(Note, code is in c#)
This answer is based on the example from the iTextSharp documentation, but converted to C#.
To make the added text span multiple pages, I found I could use ColumnText.HasMoreText(ct.Go()) to tell me if there was more text than could fit on the current page. You can then save the current page, re-create a new page template, and move the columntext to the new page. Below this is in a function called CheckForNewPage:
private bool CheckForNewPage(PdfCopy copy, ref PdfImportedPage page, ref PdfCopy.PageStamp stamp, ref PdfReader templateReader, ColumnText ct)
{
if (ColumnText.HasMoreText(ct.Go()))
{
//Write current page
stamp.AlterContents();
copy.AddPage(page);
//Start a new page
ct.SetSimpleColumn(36, 36, 559, 778);
templateReader = new PdfReader("template.pdf");
page = copy.GetImportedPage(templateReader, 1);
stamp = copy.CreatePageStamp(page);
ct.Canvas = stamp.GetOverContent();
ct.Go();
return true;
}
return false;
}
This should be called each time text is added to the ct variable.
If CheckForNewPage returns true you can then increment the page count, and reset the y variable to the top of the new page so that link annotation is in the correct place on the new page.
e.g.
var tocPageCount = 0;
var para = new iTextSharp.text.Paragraph(documentName);
ct.AddElement(para);
ct.Go();
if (CheckForNewPage(context, copy, ref page, ref stamp, ref tocReader, ct))
{
tocPageCount++;
y = 778;
}
//Add link annotation
action = PdfAction.GotoLocalPage(d.DocumentID.ToString(), false);
link = new PdfAnnotation(copy, TOC_Page.Left, ct.YLine, TOC_Page.Right, y, action);
stamp.AddAnnotation(link);
y = ct.YLine;
This creates the pages correctly. The below code adapts the end of ToC2 example for re-ordering the pages, in order to handle more than 1 page.
var rdr = new PdfReader(baos.toByteArray());
var totalPageCount = rdr.NumberOfPages;
rdr.SelectPages(String.Format("{0}-{1}, 1-{2}", totalPageCount - tocPageCount +1, totalPageCount, totalPageCount - tocPageCount));
PdfStamper stamper = new PdfStamper(rdr, new FileStream(outputFilePath, FileMode.Create));
stamper.Close();
By re-using the CheckForNewPage function, you should be able to add any content to new pages you create, and have it span multiple pages. If you don't need the annnotations you call CheckForNewPage in a loop at the end of adding all your content (just don't call ct.Go() beforehand).

PDFsharp: Is there a way to generate "Page X of Y" in the header of the page?

It seems rather simple, but I can't find something like getPageCount() in the API. I can get it to return the current page, but not the total number of pages. Perhaps I'm missing it?
I would like to somehow be able to print 'Page 1 of 9' at the top of every page, where '1' of course is the current page number.
Make sure to include the using MigraDoc.DocumentObjectModel; statement in your class.
Document document = new Document();
Section section = document.AddSection();
Paragraph paragraph = new Paragraph();
paragraph.AddText("Page ");
paragraph.AddPageField();
paragraph.AddText(" of ");
paragraph.AddNumPagesField();
section.Headers.Primary.Add(paragraph);
With PDFsharp it's up to you.
I presume you are using MigraDoc: With MigraDoc you can add a page header. Add paragraph.AddPageField() for the current page number and paragraph.AddNumPagesField() for the total page count.
Sample that uses AddPageField
Code snippet from the sample:
// Create a paragraph with centered page number. See definition of style "Footer".
Paragraph paragraph = new Paragraph();
paragraph.AddTab();
paragraph.AddPageField();
// Add paragraph to footer for odd pages.
section.Footers.Primary.Add(paragraph);
// Add clone of paragraph to footer for odd pages. Cloning is necessary because an object must
// not belong to more than one other object. If you forget cloning an exception is thrown.
section.Footers.EvenPage.Add(paragraph.Clone());
Code snippet that sets the tab stop (assuming DIN A 4 with a body with of 16 cm):
style = document.Styles[StyleNames.Footer];
style.ParagraphFormat.AddTabStop("8cm", TabAlignment.Center);
Both snippets taken from the linked site. Sample code is also available for download.
I know this question is old and has an accepted answer, however the question comes up among the first when searching for a PDFsharp solution.
For the record, achieving this in PDFsharp is easy. The PdfDocument class, found under the PdfSharp.Pdf namespace contains a collection of pages (PdfDocument.Pages). All you have to do is iterate through the collection and add the page counter somewhere on every page, using a XGraphics object, that you can instantiate using XGraphics.FromPdfPage(PdfPage).
using PdfSharp.Pdf; // PdfDocument, PdfPage
using PdfSharp.Drawing; // XGraphics, XFont, XBrush, XRect
// XStringFormats
// Create a new PdfDocument.
PdfDocument document = new PdfDocument();
// Add five pages to the document.
for(int i = 0; i < 5; ++i)
document.AddPage();
// Make a font and a brush to draw the page counter.
XFont font = new XFont("Verdana", 8);
XBrush brush = XBrushes.Black;
// Add the page counter.
string noPages = document.Pages.Count.ToString();
for(int i = 0; i < document.Pages.Count; ++i)
{
PdfPage page = document.Pages[i];
// Make a layout rectangle.
XRect layoutRectangle = new XRect(0/*X*/, page.Height-font.Height/*Y*/, page.Width/*Width*/, font.Height/*Height*/);
using (XGraphics gfx = XGraphics.FromPdfPage(page))
{
gfx.DrawString(
"Page " + (i+1).ToString() + " of " + noPages,
font,
brush,
layoutRectangle,
XStringFormats.Center);
}
}
It's worth noting that if a XGraphics object already exists for a given page, before creating a new one, the old one needs to be disposed. This would fail:
PdfDocument document = new PdfDocument();
PdfPage page = document.AddPage();
XGraphics gfx1 = XGraphics.FromPage(page);
XGraphics gfx2 = XGraphics.FromPage(page);
It is worth noting that AddSectionPagesField() also exists. In this way 'Y' will be the number of pages of the section instead of the number of pages of the entire document.
It finds its use when you generate many different documents for one print and you want to separate page counting. I hope it is understandable.
So then you can also use:
Paragraph paragraph = new Paragraph();
paragraph.AddText("Page");
paragraph.AddPageField();
paragraph.AddText(" of ");
paragraph.AddSectionPagesField();
// Add paragraph to header for odd pages.
section.Headers.Primary.Add(paragraph);
// Add clone of paragraph to header for odd pages. Cloning is necessary because an object must
// not belong to more than one other object. If you forget cloning an exception is thrown.
section.Headers.EvenPage.Add(paragraph.Clone());
Similarly just for footer use:
section.Footers.Primary.Add(paragraph);
section.Footers.EvenPage.Add(paragraph.Clone());
here's how you can fix it
Paragraph foot = sec.Footers.Primary.AddParagraph();
foot.AddText("Page ");
foot.AddPageField();
foot.AddText(" of ");
foot.AddNumPagesField();

Remove PDF page breaks

How can I remove page breaks from a pdf, so the output would be a single 'page' PDF? So if a normal page is 400x900 and I have 4 pages, a resulting file would be 1600x900. I previously did this for Tif files (Remove page breaks in multi-page tif to make one long page), but would like to do it with PDF. Could I possibly convert to ps, remove whatever code means 'page break', then convert back to pdf?
This can be done in the iTextSharp library by using a single columned PdfTable and dynamically changing the size of the document dependent upon the number of pages.
You'll of course need a few references to the iTextSharp DLL found here
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.IO;
Here's a simple example:
public static void MergePages()
{
using (PdfReader reader = new PdfReader(#"C:\Users\cmilne\Desktop\AA0081913.pdf"))//Original PDF containing page breaks.
{
int pages = reader.NumberOfPages;
float postProcessPageHeight = 0;
float postProcessPageWidth = 0;
for (int p = 1; p <= bill.PageCount; p++)
{
var size = bill.PdfReader.GetPageSize(p);
postProcessPageHeight += (size.Height);
if (size.Width > postProcessPageWidth)
postProcessPageWidth = (size.Width);
}
var rect = new Rectangle(postProcessPageWidth, postProcessPageHeight);
using (Document document = new Document(rect, 0, 0, 0, 0))
{
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(#"C:\Users\cmilne\Desktop\AA0081913_NEW.pdf", FileMode.Create)); //Declare location\name of new PDF not containing page breaks.
document.Open();
PdfImportedPage page;
PdfPTable table = new PdfPTable(1);
table.WidthPercentage = 100;
for (int i = 1; i <= pages; i++)
{
page = writer.GetImportedPage(reader, i);
table.AddCell(iTextSharp.text.Image.GetInstance(page));
}
document.Add(table);
document.Close();
}
}
}
The ending page size must be smaller than 14400 by 14400. (This is all that iTextSharp allows) An 8 1/2 x 11 PDF at a common resolution would make the max about 18 pages.
Use the iTextSharp C# library. It gives you a lot of options to manipulate PDFs. I've used it before when I had to write an import application for a closed-source document repository. It worked like a charm. The only downside is their documentation is kind of spotty because they want you to purchase their book. You can browser their Java API though for free since its almost identical to the C#, and just play around with it to find the C# version.
iText: http://itextpdf.com/

How to add a PDF form field (or a text) and link in the page bottom of a page of an existing PDF document using iTextSharp?

I have an existing PDF document named as aa.pdf. This PDF document has 3 pages. I'd like to add a PDF form field (or a text) at the page bottom of the first page in aa.pdf using iTextSharp.
Meanwhile, I also hope that the PDF form field added (or the text added) can link into another page of aa.pdf. For example, after I click the PDF form field (or the text) located in the first page of aa.pdf,this PDF document skips into the second page.
How can I realize the aboved functionalities using iTextSharp?
Thanks.
To create links within a PDF you use a PdfAction which can be set on a Chunk which can optionally be added to a Paragraph. There are several different types of actions that you can choose from, the two that you are probably interested in are the NEXTPAGE action and/or the GotoLocalPage action. The first item does what it says and goes to the next page. This one is nice because you don't have to worry about figuring out what page number you are on. The second item allows you to specify the specific page number to go to. In its simplest form you can do:
Chunk ch = new Chunk("Go to next page").SetAction(new PdfAction(PdfAction.NEXTPAGE));
This creates a Chunk that you can add in whatever way you want. When working with an existing PDF there's several different ways to add text to a page. One way it to use a ColumnText object which has a method called SetSimpleColumn that allows you to define a simple rectangle that you can add elements to.
Lastly, PDF readers don't automatically treat links differently within a PDF except to give a different cursor when hovering. More specifically, unlike a webpage where hyperlinks are turned a different color, PDFs don't change the color of links unless you tell them to, so this should be kept in mind when creating them. Also, when modifying a PDF you generally never want to overwrite the existing PDF during the process because that would be writing to something that your reading from. Sometimes it works, more often then not it breaks, sometimes subtly. Instead, write to a second file and when you are completely done, erase the first file and rename the second file.
The code below is a full working C# 2010 WinForms app targeting iTextSharp 5.1.2.0. The first part of the code creates a sample PDF called "aa.pdf" on the desktop. If you already have that file you can comment this section out but its in here so others can reproduce this example. The second part creates a new file called "bb.pdf" based on "aa.pdf". It adds two text links to the bottom of the first page. The first link advances the PDF to just the next page while the second link advances the PDF to a specific page number. See the comments in the code for specific implementation details.
using System;
using System.IO;
using System.Windows.Forms;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
//Files that we'll be working with
string inputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "aa.pdf");
string outputFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "bb.pdf");
//Create a standard PDF to test with, nothing special here
using (FileStream fs = new FileStream(inputFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
using (Document doc = new Document(PageSize.LETTER)) {
using (PdfWriter writer = PdfWriter.GetInstance(doc, fs)) {
doc.Open();
//Create 10 pages with labels on each page
for (int i = 1; i <= 10; i++) {
doc.NewPage();
doc.Add(new Paragraph(String.Format("This is page {0}", i)));
}
doc.Close();
}
}
}
//For the OP, this is where you would start
//Declare some variables to be used later
ColumnText ct;
Chunk c;
//Bind a reader to the input file
PdfReader reader = new PdfReader(inputFile);
//PDFs don't automatically make hyperlinks a special color so we're specifically creating a blue font to use here
iTextSharp.text.Font BlueFont = FontFactory.GetFont("Arial", 12, iTextSharp.text.Font.NORMAL, iTextSharp.text.BaseColor.BLUE);
//Create our new file
using (FileStream fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
//Bind a stamper to our reader and output file
using (PdfStamper stamper = new PdfStamper(reader, fs)) {
Chunk ch = new Chunk("Go to next page").SetAction(new PdfAction(PdfAction.NEXTPAGE));
//Get the "over" content for page 1
PdfContentByte cb = stamper.GetOverContent(1);
//This example adds a link that goes to the next page
//Create a ColumnText object
ct = new ColumnText(cb);
//Set the rectangle to write to
ct.SetSimpleColumn(0, 0, 200, 20);
//Add some text and make it blue so that it looks like a hyperlink
c = new Chunk("Go to next page", BlueFont);
//Set the action to go to the next page
c.SetAction(new PdfAction(PdfAction.NEXTPAGE));
//Add the chunk to the ColumnText
ct.AddElement(c);
//Tell the system to process the above commands
ct.Go();
//This example add a link that goes to a specific page number
//Create a ColumnText object
ct = new ColumnText(cb);
//Set the rectangle to write to
ct.SetSimpleColumn(200, 0, 400, 20);
//Add some text and make it blue so that it looks like a hyperlink
c = new Chunk("Go to page 3", BlueFont);
//Set the action to go to a specific page number. This option is a little more complex, you also have to specify how you want to "fit" the document
c.SetAction(PdfAction.GotoLocalPage(3, new PdfDestination(PdfDestination.FIT), stamper.Writer));
//Add the chunk to the ColumnText
ct.AddElement(c);
//Tell the system to process the above commands
ct.Go();
}
}
this.Close();
}
}
}

Categories

Resources