Generate table of content using itextsharp - c#

What I am doing is to generate a pdf booklet from database. I need go generate a content table with page numbers. E.g there are two chapters with page number like:
=============================
Content table
Chapter 1 ----- 3
Chapter 2 ----- 17
=============================
The text "Chapter 1 ----- " is normal paragraph. But the page number "3" has to be produced using PdfTemplate because it can only be known later. But the pdfTemplate is absolutely positioned. How can I know where to position the PdfTemplate? Am I right on this ? How could I figure this out or should I use other methods?

I've extracted a bit of code to get you on your way.. This code allows you to place text anywhere on a page using an x and y. You may actually want to use iTextSharp's built in paragraph and margin support, but this will be useful, just needs converting to C#
Dim stamper As PdfStamper
Dim templateReader As PdfReader = New PdfReader(yourFileName)
Dim currentPage As PdfImportedPage = stamper.GetImportedPage(templateReader, 1)
stamper.InsertPage(1, PageSize.A4)
Dim cb As PdfContentByte = stamper.GetOverContent(1)
cb.AddTemplate(currentPage, 0, 0)
Look this next bit with each element you want to add..
cb.BeginText()
cb.SetFontAndSize(bf, 12)
cb.SetColorFill(color) 'create a color object to represent the colour you want
cb.ShowTextAligned(1, "Content Table", x, y, 0) 'pass in the x & y of the element
cb.EndText()

Related

Write text to fixed position in paragraph with iText7

I try to write a pdf file with a header, logo and table using iText7 in c#.
I never used iText7 before and therefore I don't know how to write text in a paragraph to a fixed position.
Right now I am just using tabstops as anchors for my text. But the problem here is, when the string is too long everything following in the line will be shifted by a tabstop and the "columns" in the header aren't aligned anymore.
The following picture is what I want too achieve:
This picture shows what happens if a string gets too long (in this example I used a long username):
Here is a code snippet I use to write one line of the header:
// generate 8 tabstops to split pdf in equal sections
List<TabStop> tabStops = new List<TabStop>();
for (uint i = 0; i < 8; i++)
{
float tabSize = pageSize.GetWidth() / 8;
tabStops.Add(new TabStop(tabSize, TabAlignment.LEFT));
}
Paragraph p = new Paragraph();
p.SetFontSize(10);
// add tabstops to paragraph for text alignment
p.AddTabStops(tabStops);
// add title of header
p.Add(title1).Add("\n");
// write line one of header
p.Add("Serie: ").Add(new Tab()).Add(info.serial.ToString())
.Add(new Tab()).Add(new Tab())
.Add("Input CSV: ").Add(new Tab()).Add(info.inputFileName)
.Add(new Tab()).Add(new Tab()).Add("Out-Series: ")
.Add(info.serial.ToString()).Add("\n");
// line 2...
p.Add("User: ").Add(new Tab()).Add(info.username)
.Add(new Tab()).Add(new Tab()).Add(new Tab())
.Add("qPCR-Datei: ").Add(new Tab()).Add(info.qpcr1FileName)
.Add(new Tab()).Add(new Tab()).Add(new Tab())
.Add("STR-Out: ").Add(strFileName).Add("\n");
I hope someone can help me show me a better way of text alignment or has information where to look at.
Another nice tip would be how I can keep linebreaks in the same tab stop section. for example if a file name gets too long (s. "STR-Out: " in picture) the linebreak will be executed but the part of the filename in the new line should stay at the tab stop behind "STR-OUT: "
Instead of Tab/Tabspace use Tables and Cells so that alignment will be proper.
Create table of column 8 size (Label, Value, space , Label, Value, Space, Label, Value)
Use this sample Code.
PdfPTable table = new PdfPTable(8);
PdfPCell cell;
cell = new PdfPCell();
cell.setRowspan(2); //only if spanning needed
table.addCell(cell);
for(int aw=0;aw<8;aw++){
table.addCell("hi");
}
Thanks #shihabudheenk for pointing me in the right direction with the idea of using a table.
Just had to adjust some code to iText7.
First thing is that
Table headerTable = new Table().SetBorder(Border.NO_BORDER);
has no effect in iText7, you have to set the option for each cell individually like:
Cell cell = new Cell().SetBorder(Border.NO_BORDER);
but here is the problem that
cell.Add()
in iText7 only accepts IBlockElement as parameter so i have too use it like this:
cell.Add(new Paragraph("text");
which is pretty annoying doing that for every cell over and over again. Therefore i used a removeBorder function as suggested here
So the final code I use to build the header looks like this:
// initialize table with fixed column sizes
Table headerTable = new Table(UnitValue.CreatePercentArray(
new[] { 1f, 1.2f, 1f, 1.8f, 0.7f, 2.5f })).SetFixedLayout();
// write headerline 1
headerTable.AddCell("Serie: ").AddCell(info.serial.ToString())
.AddCell("Input CSV: ")
.AddCell(info.inputFileName)
// write remaining lines...
....
// remove boarder from all cells
removeBorder(headerTable);
private static void removeBorder(Table table)
{
foreach (IElement iElement in table.GetChildren())
{
((Cell)iElement).SetBorder(Border.NO_BORDER);
}
}

Can't change /Contents of annotation

I'm trying to change the text in some PDF annotations using iTextSharp. Here is my code:
void changeAnnotations(string inputPath, string outputPath)
{
PdfReader pdfReader = new PdfReader(inputPath);
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(outputPath, FileMode.Create));
//get the PdfDictionary of the 1st page
PdfDictionary pageDict = pdfReader.GetPageN(1);
//get annotation array
PdfArray annotArray = pageDict.GetAsArray(PdfName.ANNOTS);
//iterate through annotation array
int size = annotArray.Size;
for (int i = 0; i < size; i++)
{
//get value of /Contents
PdfDictionary dict = annotArray.GetAsDict(i);
PdfString contents = dict.GetAsString(PdfName.CONTENTS);
//check if /Contents key exists
if (contents != null)
{
//set new value
dict.Put(PdfName.CONTENTS, new PdfString("value has been changed"));
}
}
pdfStamper.Close();
}
When I open the output file in Adobe Reader, none of the text has changed in any of the annotations. How should I be setting the new value in an annotation?
UPDATE: I've found that the value is being changed in the popup box that appears when I click on the annotation. And in some cases, when I modify this value in the popup box, the change is then applied to the annotation.
As the OP clarified in a comment:
This annotation is a FreeText, how do I find and change the text that's displayed in this text box?
Free text annotations allow a number of mechanisms to set the displayed text:
A pre-formatted appearance stream, referenced by the N entry in the AP dictionary
A rich text string with a default style string given in RC and DS respectively
A default appearance string applied to the contents given in DA and Contents respectively
(For details cf. the PDF specification ISO 32000-1 section 12.5.6.6 Free Text Annotations)
If you want to change the text using one of these mechanisms, make sure you remove or adjust the contents of the entries for the other mechanisms; otherwise your change might not be visible or even visible on some viewers but not visible on others.
I can't figure out how to determine if there is an appearance stream. Is that the /AP property? I checked that for one of the annotations and it's a dictionary with a single entry whose value is 28 0 R.
So that one of the annotations indeed comes with an appearance stream. The single entry whose value is 28 0 R presumably has the N name to indicate the normal appearance. 28 0 R is a reference to the indirect object with object number 28 and generation 0.
If you want to change the text content but do not want to deal with the formatting details, you should remove the AP entry.

Bookmark to specific page using iTextSharp 4.1.6

I want to add a bookmark pointing to a specific page within a document. Adding bookmarks from other PDF files I'm merging with code similar to that below works fine, but when I copied it to add custom bookmarks to non-bookmarked items it fails:
var bookmarks = new ArrayList();
var writer = new PdfCopy(document, memorystream);
// ...
var uni = new Hashtable();
uni.Add("Action", "GoTo");
uni.Add("Title", "Awesome Unicorn pic");
uni.Add("Page", "8 XYZ 0 0 0");
bookmarks.Add(uni);
// ...
writer.Outlines = bookmarks;
But apparently ("Page", "8 XYZ 0 0 0") does not reference Page 8 but rather Section 8 or something like that. Is there an alternative Action I could use to point to an arbitrary page? Or some other method?
Looks like the PDF coordinate system messed with my feeble human brain. Turns out that ("Page", "8 XYZ 0 0 0"); actually does reference page 8, but "XYZ 0 0 0" does not reference the top left point on a page, but rather the bottom left point. So when clicked, a bookmark like this unexpectedly takes you to page two. Awesome.
The code below works as expected, because it gets the height of the first page and uses that to link to the top of the page. The code is gathered from different places around my source, so it's not very "together" but still, it works.
var bookmarks = new ArrayList();
var rdr = new PdfReader(first);
var doc = new Document(rdr.GetPageSizeWithRotation(1));
var wri = new PdfCopy(doc, memorystream);
var temp = wri.GetImportedPage(rdr, 1); // get 1st page
var h = temp.Height; // get height of 1st page
// Add first item to bookmarks.
var test = new Hashtable();
test.Add("Action", "GoTo");
test.Add("Title", "Page1 0 H 0");
test.Add("Page", "1 XYZ 0 "+h+" 0"); // use height of 1st page
bookmarks.Add(test);
// Do your worst and afterwards set the bookmarks to Outline. So yeah.
wri.Outlines = bookmarks;

How to align content in columns using spacing, tabs or padding?

i'm trying to create a pdf looks like this
but when i try string padding, it looks like this in pdf file
here is the part of the c# code i tried. myExcelData is filled from excel file.
for (int i = 0; i < 15; i++)
{
Chunk cSira = new Chunk((i + 1).ToString().PadRight(10), icerikFont);
Chunk cHizmet = new Chunk(myExcelData.Tables[0].Rows[i][6].ToString().PadRight(80), icerikFont);
Chunk cAdet = new Chunk(myExcelData.Tables[0].Rows[i][1].ToString().PadRight(10), icerikFont);
Chunk cBirimFiyat = new Chunk(myExcelData.Tables[0].Rows[i][2].ToString().PadRight(20), icerikFont);
Chunk cTutar = new Chunk(myExcelData.Tables[0].Rows[i][3].ToString().PadRight(20), icerikFont);
d.Add(cSira);
d.Add(cHizmet);
d.Add(cAdet);
d.Add(cBirimFiyat);
d.Add(cTutar);
d.Add(Chunk.NEWLINE);
}
There are two ways to do this:
use a monospaced font (e.g. Courier). Currently you're using a font of which the width of the different characters is different.
download chapter 2 of my book and learn how to use TAB CHUNKS (look for that title on page 48).
Use a PdfPTable as mkl indicates in his comment (maybe you were overlooking the obvious)
If you need a code snippet for option 2, take a look at the DirectorOverview3 example. The result looks like this. If you don't understand Java, read the C# example.

PowerPoint Programming: Issues while trying to access ruler margins

I am trying to access the indentation levels of various bulleted list items. So I created a simple function:
private float[] findIndentSpacing(TextRange t, int level) {
if(level == 1) {
RulerLevel rl = t.Parent.Ruler.Levels(2);
//bullet must start at 0 on the first level for now
return new float[2] { 0, rl.LeftMargin * Settings.Scaler() };
} else {
RulerLevel rl = t.Parent.Ruler.Levels[level];
return new float[2] { rl.FirstMargin * Settings.Scaler(), rl.LeftMargin * Settings.Scaler() };
}
}
So that first if statement is a work around. The first level LeftMargin always returns: -2.14748365E+9 for some reason. I've tried to just grab levels after the first and they return actual values. That being said, after one level has been accessed all other levels change and become equal. For example if I try to access: t.Parent.Ruler.Levels[2].FirstMargin, then for some reason t.Parent.Ruler.Levels[3].FirstMargin become the same, and so forth. LeftMargin changes also.
I've tried accessing the ruler object in different ways: by selection, by shape, by text and every way I've thought to try the result is the same.
Ideas?
More info:
I read the following threads, but they are more about writing than reading, but I feel like the problem is similar: PowerPoint Programming: Indentation with Ruler margin levels not working?
http://answers.microsoft.com/en-us/office/forum/office_2007-customize/why-shapetextframerulerlevelsi-cant-set-the-bullet/9eac3e46-b13b-433e-b588-216ead1d9c1a?tab=AllReplies#tabs
I made this one: http://answers.microsoft.com/en-us/office/forum/office_2010-customize/find-bullet-spacing-information-in-an-automated/4525b6b8-6331-4f33-8127-789ea3641589?page=1&tm=1336535132591
In 2007 and 2010 I think you'll need to work with TextRange2 and TextFrame2 objects.
In PPT 2003 and previous, the TextFrame could have 5 indent levels, and all paragraphs at a given indent level shared the same LeftMargin and FirstMargin.
From 2007 on, TextFrames can have up to 9 indent levels, and each paragraph can have its own Left/First margins, independent of the margins set on other paragraphs at the same indent level.
Try this in PPT's VBA IDE. Select the text you're looking at then:
Sub Levels()
Dim oSh as Shape
Dim x As Long
Set oSh = ActiveWindow.Selection.ShapeRange(1)
With oSh.TextFrame2.Ruler
For x = 1 to .Count
Debug.Print .Levels(x).FirstMargin
Debug.Print .Levels(x).LeftMargin
Next
End With
End Sub

Categories