I'm generating a clothing label as pdfs using iText. I'm trying to add a dashed line as a folding line in every page. But when I added a dashed line existing strokes replace to dashed lines? Any idea how to stop this?
I tried adding dashed lines when creating the pdf and after creating the pdf. But none of those works.
This is my code.
string inputPDF = "C:\\Users\\User\\Documents\\visual studio 2017\\Projects\\iTextSharpExample\\iTextSharpExample\\pdf\\Label_dynamicLive_SampleTemplate.pdf";
string outputPDF = "C:\\Users\\User\\Documents\\visual studio 2017\\Projects\\iTextSharpExample\\iTextSharpExample\\pdf\\Label_dynamicLive_SampleTemplate_foldline.pdf";
PdfReader reader = new PdfReader(inputPDF);
using (var fileStream = new FileStream(outputPDF, FileMode.Create, FileAccess.Write))
{
var document = new Document(reader.GetPageSizeWithRotation(1));
var writer = PdfWriter.GetInstance(document, fileStream);
document.Open();
for (var i = 1; i <= reader.NumberOfPages; i++)
{
document.NewPage();
var baseFont = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
var importedPage = writer.GetImportedPage(reader, i);
var contentByte = writer.DirectContent;
//line start
float moveto_x = 0 + 1;
float lineto_x = 20 - 1;
float moveto_y = (110 / 2) + 5;
float lineto_y = (110 / 2) + 5;
float moveto_x2 = 0 + 1;
float lineto_x2 = 20 - 1;
float moveto_y2 = (110 / 2) - 5;
float lineto_y2 = (110 / 2) - 5;
float lineWidth = 0.5f;
float unitsOn = 5;
float unitsOff = 1;
float phase = 2;
moveto_x = iTextSharp.text.Utilities.MillimetersToPoints(moveto_x);
moveto_y = iTextSharp.text.Utilities.MillimetersToPoints(moveto_y);
contentByte.MoveTo(moveto_x, moveto_y);
lineto_x = iTextSharp.text.Utilities.MillimetersToPoints(lineto_x);
lineto_y = iTextSharp.text.Utilities.MillimetersToPoints(lineto_y);
contentByte.LineTo(lineto_x, lineto_y);
contentByte.SetLineWidth(lineWidth);
contentByte.SetLineDash(unitsOn, unitsOff, phase);
contentByte.Stroke();
//line end
contentByte.AddTemplate(importedPage, 0, 0);
}
document.Close();
writer.Close();
}
I expected to write dashed lines in every page in given ordinates. But it turned to replace straight lines to dashed lines. Any idea how to add a lines without replacing existing?
The line dash (just like the line width, fill and stroke colors, and many other properties) is part of the "pdf graphics state".
To get back an earlier graphics state, pdfs support a stack of graphics states. When the current state is what you want to get back to later, you push the current state on that stack. And when you want to get back to that state, you pop it off of that stack.
The instruction for pushing is called save-state, the instruction for popping restore-state. The matching PdfContentByte methods are SaveState() and RestoreState().
Thus, start with contentByte.SaveState(), then do your stuff, then end with contentByte.RestoreState().
Add an aside, your code generates instructions in an invalid order:
contentByte.MoveTo(moveto_x, moveto_y);
contentByte.LineTo(lineto_x, lineto_y);
contentByte.SetLineWidth(lineWidth);
contentByte.SetLineDash(unitsOn, unitsOff, phase);
contentByte.Stroke();
Here you first create a path, then set line width and line dash, and then stroke the path. Strictly speaking, though, between the creation of a path and the instruction drawing it there may at most be an instruction to combine the path with the clip path but nothing else.
Most pdf viewers don't insist on that, though, so you'll probably not have concrete problems because of that.
But if your pdfs are validated, this invalid order may be reported as error.
Thus, first set the parameters, then create the path and draw it.
contentByte.SaveState();
contentByte.SetLineWidth(lineWidth);
contentByte.SetLineDash(unitsOn, unitsOff, phase);
contentByte.MoveTo(moveto_x, moveto_y);
contentByte.LineTo(lineto_x, lineto_y);
contentByte.Stroke();
contentByte.RestoreState();
Related
I have been using iText7 latest .net package to create watermark annotation on pdf document. I see for most of the noraml pdf files watermark is stamped properly in the desired position however for some special 3d(dimensional) pdf files the watermark is strangely not appearning properly, what I believe that some part of the watermark is gettting cut by the other 3d layers. Here is the screenshot of distortion experienced. I am trying to understand and solve this problem but unable to get any clue. Can someone help? Here is the sample pdf in case you want to take a look.
Here is the very short relevant code which I am using to embed watermark.
float watermarkTrimmingRectangleWidth = 500;
float watermarkTrimmingRectangleHeight = 500;
float formWidth = 500;
float formHeight = 500;
float formXOffset = -40;
float formYOffset = 0;
float xTranslation = 50;
float yTranslation = 25;
double rotationInRads = Math.PI / 4.2;
PdfDocument pdfDoc = new PdfDocument(new PdfReader(filePath), new PdfWriter(destinationfile));
var numberOfPages = pdfDoc.GetNumberOfPages();
PdfPage page = null;
for (var i = 1; i <= numberOfPages; i++)
{
page = pdfDoc.GetPage(i);
Rectangle ps = page.GetPageSize();
float bottomLeftX = ps.GetWidth() / 2 - watermarkTrimmingRectangleWidth / 2;
float bottomLeftY = ps.GetHeight() / 2 - watermarkTrimmingRectangleHeight / 2;
Rectangle watermarkTrimmingRectangle = new Rectangle(bottomLeftX, bottomLeftY, ps.GetWidth(), watermarkTrimmingRectangleHeight);
PdfWatermarkAnnotation watermark = new PdfWatermarkAnnotation(watermarkTrimmingRectangle);
AffineTransform transform = new AffineTransform();
transform.Translate(xTranslation, yTranslation);
transform.Rotate(rotationInRads);
PdfFixedPrint fixedPrint = new PdfFixedPrint();
watermark.SetFixedPrint(fixedPrint);
Rectangle formRectangle = new Rectangle(formXOffset, formYOffset, formWidth, formHeight);
//Observation: font XObject will be resized to fit inside the watermark rectangle
PdfFormXObject form = new PdfFormXObject(formRectangle);
PdfExtGState gs1 = new PdfExtGState().SetFillOpacity(0.8f).SetStrokeOpacity(1.5f);
PdfCanvas canvas = new PdfCanvas(form, pdfDoc);
float[] transformValues = new float[6];
transform.GetMatrix(transformValues);
canvas.SaveState()
.BeginText().SetExtGState(gs1)
.SetTextMatrix(transformValues[0], transformValues[1], transformValues[2], transformValues[3], transformValues[4], transformValues[5])
.SetFontAndSize(font, fontSize)
.ShowText("Restricted Internal")
.EndText()
.RestoreState();
canvas.Release();
watermark.SetAppearance(PdfName.N, new PdfAnnotationAppearance(form.GetPdfObject()));
watermark.SetFlags(PdfAnnotation.READ_ONLY);
page.AddAnnotation(watermark);
}
page?.Flush();
pdfDoc.Close();
Your watermark annotation is drawn on top of some form fields.
Because the display order for page elements is page content, annotations, form fields, your annotation appearance is blocked by the form fields.
I have a task of adding watermarks to a lot of existing PDFs and I use iText7 for this in C#. The result can be seen in this picture (Blank pdf used)
It renders fine everywhere and prints perfectly from both Chrome and Edge. However, when printed from Adobe Acrobat Reader, this is what happens:
Anyone knowing more about PDF than I, who can help with this issue? I am using version 7.1.13 of iText.
The test pdf is available here:
https://potanteststorage.blob.core.windows.net/pdf/Test.pdf
C# Code:
public static void AddProductionWatermarks(string sourceFile, string destinationPath)
{
float watermarkTrimmingRectangleWidth = 75;
float watermarkTrimmingRectangleHeight = 250;
//Custom text
float formWidth = 75;
float formHeight = 250;
float formXOffset = 0;
float formYOffset = 0;
float xTranslation = 50;
float yTranslation = 0;
double rotationInRads = Math.PI / 2;
PdfFont font = PdfFontFactory.CreateFont(StandardFonts.TIMES_ROMAN);
float fontSize = 12;
PdfDocument pdfDoc = new PdfDocument(new PdfReader(sourceFile), new PdfWriter(destinationPath));
var numberOfPages = pdfDoc.GetNumberOfPages();
PdfPage page = null;
for (var i = 1; i <= numberOfPages; i++)
{
page = pdfDoc.GetPage(i);
Rectangle ps = page.GetPageSize();
//PRODUCTION watermark -------------------------------------------------------
float prodBottomLeftX = -20;
float prodBottomLeftY = ps.GetHeight() / 2;
Rectangle prodWatermarkTrimmingRectangle = new Rectangle(prodBottomLeftX, prodBottomLeftY, watermarkTrimmingRectangleWidth, watermarkTrimmingRectangleHeight);
PdfWatermarkAnnotation prodWatermark = new PdfWatermarkAnnotation(prodWatermarkTrimmingRectangle);
AffineTransform transform2 = new AffineTransform();
transform2.Translate(xTranslation, yTranslation);
transform2.Rotate(rotationInRads);
PdfFixedPrint fixedPrint2 = new PdfFixedPrint();
prodWatermark.SetFixedPrint(fixedPrint2);
PdfFormXObject form2 = new PdfFormXObject(formRectangle);
PdfCanvas canvas2 = new PdfCanvas(form2, pdfDoc);
transform2.GetMatrix(transformValues);
canvas2.SaveState()
.BeginText().SetColor(new DeviceRgb(255, 36, 0), true)
.SetTextMatrix(transformValues[0], transformValues[1], transformValues[2], transformValues[3], transformValues[4], transformValues[5])
.SetFontAndSize(font, fontSize)
.ShowText("PRODUCTION")
.EndText()
.RestoreState();
canvas2.Release();
prodWatermark.SetAppearance(PdfName.N, new PdfAnnotationAppearance(form2.GetPdfObject()));
prodWatermark.SetFlags(PdfAnnotation.PRINT);
page.AddAnnotation(prodWatermark);
}
page?.Flush();
pdfDoc.Close();
}
You use FixedPrint dictionaries in your annotations:
PdfFixedPrint fixedPrint2 = new PdfFixedPrint();
prodWatermark.SetFixedPrint(fixedPrint2);
This additional entry for Watermark annotations specifies how this annotation shall be drawn relative to the dimensions of the target media during printing. Thus, you ask for special treatment of the watermark during printing.
So if you indeed want special treatment during printing (merely not the current treatment), simply set the PdfFixedPrint properties accordingly.
If you don't want any special treatment during printing, don't set a PdfFixedPrint object at all.
I'm trying the create a multiple-page pdf using iTextSharp, but I'm having some issue creating a loop to have more than a single page.
My code below is working well for one page; however, my content will not fit into a single page. Thanks.
How can I write a loop to write contents on the first pdf page and the remaining to the second, third, etc...So far, I'm only seeing one page. Thank you.
int height = 600;
int totalPage = 1;
int oldPage = 1;
bool cons = false;
bool st = false;
foreach (string al in combo)
foreach (string al in combo) //my loop to write on the pdf but "combo" has 100 lines, which would fit into a single page.
{
string[] word = al.Split(',');
int strIndex = combo.IndexOf(al);
if (al.Length != 0)
{
if (word[0].ToString().ToUpper().Contains("(REV")==true && cons == false)
{
cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 11);
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(PdfContentByte.ALIGN_LEFT, "CONSTRUCTION PRINTS", 80, height, 0);
cb.EndText();
height = height - 20;
cons = true;
}
if (word[0].ToString().ToUpper().Contains("(REV")==false && st == false)
{
cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 11);
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(PdfContentByte.ALIGN_LEFT, "SAG & TENSION CHARTS", 80, height, 0);
cb.EndText();
height = height - 20;
st = true;
}
cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 11);
totalPage = totalPage + oldPage;
// write the text in the pdf content
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(PdfContentByte.ALIGN_LEFT, word[0].ToString().ToUpper(), 80, height, 0);
cb.EndText();
// write the text in the pdf content
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "......................................................................................", 335, height, 0);
cb.EndText();
// write the text in the pdf content
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(PdfContentByte.ALIGN_RIGHT, totalPage.ToString(), 500, height, 0);
cb.EndText();
oldPage = Convert.ToInt32(word[1]);
}
height = height - 20;
}
//// create the new page and add it to the pdf
// reader = new PdfReader(oldFile);//old file
PdfImportedPage page = writer.GetImportedPage(reader, 1);
cb.AddTemplate(page, 0, 0);
//// close the streams and voilá the file should be changed :)
document.Close();
reader.Close();
fs.Close();
writer.Close();
Please go to the official documentation and click Q&A to go to the most frequently asked questions. Pick the Getting started category. The first thing you'll see, is the most popular iText example (which I am porting to C# for your convenience):
// step 1
Document document = new Document();
// step 2
FileStream fs = new FileStream("hello.pdf", FileMode.Create);
PdfWriter.GetInstance(document, fs);
// step 3
document.Open();
// step 4
document.Add(new Paragraph("Hello World!"));
// step 5
document.Close();
In your code, you are adding text at absolute positions by introducing PDF syntax, such as BeginText() (BT) / EndText() (ET) / SetFontAndSize() (Tf). This is creating PDF the hard way. If is very easy to make mistakes, as shown in this question: What is causing syntax errors in a page created with iText?
If you don't know the PDF reference (ISO 32000-1) by heart, you should avoid code like this. Instead you can use objects such as Paragraph, List, PdfPTable... The beauty of adding these objects to a Document using the Add() method, is that a new page gets triggered automagically as soon as a page is full. You can also trigger a new page yourself using:
document.NewPage();
If you want to add text at absolute positions, iText offers convenience methods and objects such as ColumnText. See my answer to Adding footer to existing PDF
You can define a Rectangle and add objects such as Paragraph, List, PdfPTable... Take a look at the answer to the question How to continue an ordered list on a second page? for inspiration:
ColumnText ct = new ColumnText(cb);
ct.AddElement(list);
Rectangle rect = new Rectangle(36, 36, 559, 806);
ct.SetSimpleColumn(rect);
int status = ct.Go();
while (ColumnText.HasMoreText(status)) {
document.NewPage();
ct.SetSimpleColumn(rect);
ct.Go();
}
The while loop is the loop you are looking for.
I am attempting to create documents that are highly-variable in length, and make them something that can be table-driven. I am trying to use iTextSharp tables exclusively to create the documents, including footer verbiage. In the code below I’m trying to pad the very last cell to the exact amount equal to the remaining space, so that my footer will be placed exactly at the bottom of the document, tight against the bottom margin. This works fine when I try to use document margins of 1, 2, or 3 inches, but when I use 5 for example, my footer begins to wrap. I think this may simply be a case of bad math. Does anyone have any suggestions to correct this (outside of using Page Events)?
(*Note: there are 2 helper methods used in creating tables and measuring the height of a paragraph, also included in addition to the main code from a button click)
(*Note: each table has SplitLate set to false, to make the content flow from page to page evenly); all tables have a specific width, and the width is locked
//declare path for the PDF file to be created
string strpath = #"C:\CROauto\Junk\MSE.pdf";
//instantiate a new PDF document
//(regular portrait format, with half-inch margins all around)
var doc = new Document(PageSize.LETTER,
iTextSharp.text.Utilities.InchesToPoints(1f),
iTextSharp.text.Utilities.InchesToPoints(1f),
iTextSharp.text.Utilities.InchesToPoints(5f),
iTextSharp.text.Utilities.InchesToPoints(.5f));
//create a PDF table
//(2 column, with no border)
PdfPTable mas_tbl = clsPDF.CreateTable(2, false, cell_padding_bottom: 0f, total_width: doc.PageSize.Width - doc.LeftMargin - doc.RightMargin);
//get base font types
BaseFont font_tb = FontFactory.GetFont(FontFactory.TIMES_BOLD).BaseFont;
BaseFont font_t = FontFactory.GetFont(FontFactory.TIMES).BaseFont;
//create regular Font objects, with varying sizes
iTextSharp.text.Font f10 = new iTextSharp.text.Font(font_t, 10);
iTextSharp.text.Font f8 = new iTextSharp.text.Font(font_t, 8);
//create regular Font objects, bold, with varying sizes
iTextSharp.text.Font fb10 = new iTextSharp.text.Font(font_tb, 10);
iTextSharp.text.Font fb8 = new iTextSharp.text.Font(font_tb, 8);
//get image
iTextSharp.text.Image da_img = iTextSharp.text.Image.GetInstance(#"C:\CROauto\Junk\img.gif");
//scale the image to a good size
da_img.ScaleAbsolute(iTextSharp.text.Utilities.InchesToPoints(.75f),
iTextSharp.text.Utilities.InchesToPoints(.75f));
//create new PDF cell to hold image
PdfPCell icell = new PdfPCell();
icell.PaddingBottom = 0f;
//make sure the "image" cell has no border
icell.Border = iTextSharp.text.Rectangle.NO_BORDER;
//add the image to the PDF cell
icell.AddElement(da_img);
//add the image cell to the table
mas_tbl.AddCell(icell);
//work with the return address
PdfPCell ra = new PdfPCell();
ra.Border = iTextSharp.text.Rectangle.NO_BORDER;
ra.PaddingBottom = 5f;
PdfPTable tblra = clsPDF.CreateTable(1, false);
tblra.HorizontalAlignment = Element.ALIGN_RIGHT;
Chunk c = new Chunk("Help Me Please", fb8);
string rtnadd = "\r\n123 iTextSharp Rd\r\nHelpMe, ST 12345\r\n\r\nstackoverflow.com";
Phrase pra = new Phrase(rtnadd, f8);
Paragraph p = new Paragraph();
p.SetLeading(1f, 1.1f);
p.Add(c);
p.Add(pra);
tblra.TotalWidth = clsPDF.GetLongestWidth(p) + ra.PaddingLeft + ra.PaddingRight + 2;
ra.AddElement(p);
tblra.AddCell(ra);
PdfPCell dummy = new PdfPCell();
dummy.PaddingBottom = 0f;
dummy.Border = iTextSharp.text.Rectangle.NO_BORDER;
dummy.AddElement(tblra);
mas_tbl.AddCell(dummy);
//create "content" table
PdfPTable t2 = clsPDF.CreateTable(1, false, Element.ALIGN_JUSTIFIED, cell_padding_bottom: 0f);
//create FileStream for the file
using (FileStream fs = new FileStream(strpath, FileMode.Create))
{
//get an instance of a PdfWriter, attached to the FileStream
PdfWriter.GetInstance(doc, fs);
//open the document
doc.Open();
string tmp = "";
for (int i = 0; i < 80; i++)
{
tmp += "The brown fox jumped over the lazy dog a whole bunch of times." + i.ToString();
}
Phrase p2 = new Phrase(tmp, f10);
t2.AddCell(p2);
p2 = new Phrase("Another paragraph", f10);
t2.AddCell(p2);
tmp = "";
PdfPTable t3 = clsPDF.CreateTable(1, false, cell_padding_bottom: 0f);
for (int i = 0; i < 150; i++)
{
tmp += "The lazy dog didn't like that very much." + i.ToString();
}
t3.AddCell(new Phrase(tmp, f10));
t2.AddCell(t3);
t2.AddCell(new Phrase("I SURE HOPE THIS WORKED", f10));
PdfPCell c2 = new PdfPCell();
c2.PaddingBottom = 0f;
c2.Border = iTextSharp.text.Rectangle.NO_BORDER;
c2.Colspan = mas_tbl.NumberOfColumns;
c2.AddElement(t2);
mas_tbl.AddCell(c2);
//work with adding a footer
//FOOTER MSE: ADD ENOUGH PADDING TO PUSH THE FOOTER TO THE BOTTOM OF THE PAGE
Paragraph fp = new Paragraph(new Phrase("Line 1 of footer\r\nLine 2 of footer\r\nhere's more of my footer text:\r\nthis project was SOOOO much fun\r\nand stuff", fb8));
//get the height of the footer
float footer_height = clsPDF.GetTotalHeightOfParagraph(fp);
Console.WriteLine("Footer height {0}", footer_height.ToString());
//get the total amount of "writeable" space per page
//(taking top and bottom margins into consideration)
float avail = doc.PageSize.Height - (doc.TopMargin + doc.BottomMargin);
//declare a variable to assist in calculating
//the total amount of "writeable" room remaining
//on the last page;
//start with with the current height of the master table
//(will do math below to calculate just what it's using
// on the last page)
float mas_tbl_last_page_height = mas_tbl.TotalHeight;
//the purpose of this loop is to start determining
//how much writeable space is left on the last page;
//this loop will subtract the "available" value from
//the total height of the master table until what's
//left is the amount of space the master table is
//using on the last page of the document only
while (mas_tbl_last_page_height > avail)
{
mas_tbl_last_page_height -= avail;
}
//to truly calculate the amount of writeable space
//remaining, subtract the amount of space that the
//master table is utilizing on the last page of
//the document, from the total amount of writeable
//space per page
float room_remaining = avail - mas_tbl_last_page_height;
//declare variable for the padding amount
//that will be used above the footer
float pad_amt = 0f;
if (room_remaining > (footer_height * 2))
{
//pad to push down
pad_amt = room_remaining - (footer_height * 2);
}
else
{
//don't use a pad
//(just let the table wrap normally)
pad_amt = 0f;
}
//declare the footer cell, and set all of it's values
PdfPCell ftcell = new PdfPCell();
ftcell.HorizontalAlignment = Element.ALIGN_JUSTIFIED;
//(use column span that is equal to the number of
// columns in the master table)
ftcell.Colspan = mas_tbl.NumberOfColumns;
ftcell.Border = iTextSharp.text.Rectangle.NO_BORDER;
ftcell.PaddingTop = pad_amt;
ftcell.PaddingBottom = 0f;
ftcell.AddElement(fp);
//add the footer cell to the master table
mas_tbl.AddCell(ftcell);
//add the master table to the document, which should contain everything
doc.Add(mas_tbl);
//close the document
doc.Close();
//HELPER METHODS
internal static PdfPTable CreateTable(int column_count,
bool include_border,
int h_align = Element.ALIGN_LEFT,
int v_align = Element.ALIGN_TOP,
float leading_multiplier = 1.1f,
float total_width = 468f,
float cell_padding_bottom = 5f,
float cell_padding_top = 0)
{
////////////////////////////////////////////////////////////////////////////////////
PdfPTable t = new PdfPTable(column_count);
//this line will keep the inner tables,
//from splitting off onto a 2nd page
//(making them only do it on wrapping)
//*NOTE: this needs to be tested thoroughly
// to make sure that rows aren't dropped!!!
t.SplitLate = false;
//used if you're adding paragraphs directly to table cells,
//instead of adding new PdfPCell objects
if (include_border == false)
{
t.DefaultCell.Border = Rectangle.NO_BORDER;
}
t.DefaultCell.PaddingLeft = 0f;
t.DefaultCell.PaddingRight = 0f;
t.DefaultCell.PaddingTop = cell_padding_top;
t.DefaultCell.PaddingBottom = cell_padding_bottom;
t.DefaultCell.HorizontalAlignment = h_align;
t.DefaultCell.VerticalAlignment = h_align;
t.TotalWidth = total_width;
t.LockedWidth = true;
t.DefaultCell.SetLeading(0, leading_multiplier);
return t;
}
internal static float GetLongestWidth(Paragraph p)
{
List<float> f = new List<float>();
foreach (Chunk c in p.Chunks)
{
string[] strarray = c.Content.Split(new string[] { "\r\n" }, System.StringSplitOptions.None);
for (int i = 0; i < strarray.Length; i++)
{
Chunk tc = new Chunk(strarray[i], c.Font);
Console.WriteLine(tc.Content + ", width: {0}", tc.GetWidthPoint().ToString());
f.Add(tc.GetWidthPoint());
}
}
return f.Max();
}
internal static float GetTotalHeightOfParagraph(Paragraph p)
{
PdfPTable t = clsPDF.CreateTable(1, false, cell_padding_bottom: 0f);
t.DefaultCell.PaddingBottom = 0f;
t.DefaultCell.PaddingTop = 0f;
t.AddCell(p);
return t.TotalHeight;
}
Solved it by using absolute positioning. I calculate the position that the footer should be in, by adding the footer height to the value of the bottom margin. To alleviate concerns about overwriting existing content, I calculate the amount of available space remaining on the last page; if it's not enough for my footer, I add a new page and post my content at the beginning of the next page.
byte[] content;
using (MemoryStream output = new MemoryStream())
{
PdfReader pdf_rdr = new PdfReader(strpath);
PdfStamper stamper = new PdfStamper(pdf_rdr, output);
PdfContentByte pcb = stamper.GetOverContent(pdf_rdr.NumberOfPages);
PdfPTable ftbl = clsPDF.CreateTable(1, false, cell_padding_bottom: 0f);
Paragraph fp = new Paragraph(new Phrase("Line 1 of footer\r\nLine 2 of footer\r\nhere's more of my footer text:\r\nthis project was SOOOO much fun\r\nand stuff", fb8));
//get the height of the footer
float footer_height = clsPDF.GetTotalHeightOfParagraph(fp);
Console.WriteLine("Footer height {0}", footer_height.ToString());
//get the total amount of "writeable" space per page
//(taking top and bottom margins into consideration)
float avail = doc.PageSize.Height - (doc.TopMargin + doc.BottomMargin);
//declare a variable to assist in calculating
//the total amount of "writeable" room remaining
//on the last page;
//start with with the current height of the master table
//(will do math below to calculate just what it's using
// on the last page)
float mas_tbl_last_page_height = mas_tbl.TotalHeight;
mas_tbl_last_page_height = mas_tbl_last_page_height % avail;
avail = avail - mas_tbl_last_page_height;
Console.WriteLine(clsPDF.GetTotalHeightOfParagraph(fp).ToString());
//float ft_top = avail - mas_tbl_last_page_height - clsPDF.GetTotalHeightOfParagraph(fp) - doc.BottomMargin;
float ft_top = doc.BottomMargin + clsPDF.GetTotalHeightOfParagraph(fp);
ftbl.AddCell(fp);
if (avail < clsPDF.GetTotalHeightOfParagraph(fp))
{
stamper.InsertPage(pdf_rdr.NumberOfPages + 1, pdf_rdr.GetPageSize(1));
pcb = stamper.GetOverContent(pdf_rdr.NumberOfPages);
ft_top = doc.PageSize.Height - doc.TopMargin;
}
ftbl.WriteSelectedRows(0, -1, doc.LeftMargin, ft_top, pcb);
// Set the flattening flag to true, as the editing is done
stamper.FormFlattening = true;
// close the pdf stamper
stamper.Close();
//close the PDF reader
pdf_rdr.Close();
content = output.ToArray();
}
//write the content to a PDF file
using (FileStream fs = File.Create(strpath))
{
fs.Write(content, 0, (int)content.Length);
fs.Flush();
}
I'm working on program that insert some kind of watermark on existing pdf document. The watermark isn't image but text generated during program's work. I need to rotate the text, send it to center of page, and those things are done, but the text has many lines. So I put new line character "\n" in Phrase but PDF prints only characters before first use of "\n". How could I overlay some text on pdf document (mainly scanned docs) and rotate it?
My source code (simplified):
PdfContentByte canvas = stamper.GetOverContent(1);
String message = "some\n multiline \n expression";
Phrase text = new Phrase(text);
ColumnText.ShowTextAligned(canvas, Element.ALIGN_MIDDLE, text, size.Width / 2, size.Height / 2, 30);
That is documented: the ShowTextAligned() method is for single lines only. If you want multiple lines, you need to use a ColumnText object, define a Rectangle using the SetSimpleColumn() method and draw the content using the Go() method.
If you want to rotate such a Rectangle, you need to create a PdfTemplate first. Render the ColumnText to this PdfTemplate and add the template to the document.
Adding the template to the document can be done with the AddTemplate() method. In this case, you need some algebra to rotate the template. This is the algebra you'll need for the parameters:
a = cosine(angle);
b = sine(angle);
c = -sine(angle);
d = cosine(angle);
e = x;
f = y;
If you can't get the algebra working, you can wrap the template inside an Image object:
Image img = Image.getInstance(template);
Now you have different methods to introduce a rotation, including a simple one that merely requires an angle in degrees.
P.S.: wrapping a template inside an image doesn't "rasterize" the text. It creates a Form XObject (as opposed to an Image XObject).
I solved the problem in another way:
using (PdfStamper stamper = new PdfStamper(reader, ms))
{
PdfContentByte canvas = stamper.GetOverContent(1);
String message = String.Format("mutli\nline\text");
}
PdfGState gState = new PdfGState();
gState.FillOpacity = 0.3f;
canvas.SetGState(gState);
using (StringReader stringReader = new StringReader(message))
{
string line;
float y = size.Height / 2 + 200;
while ((line = stringReader.ReadLine()) != null)
{
Phrase p = new Phrase(line,FontFactory.GetFont(FontFactory.HELVETICA, 35));
ColumnText.ShowTextAligned(canvas, Element.ALIGN_CENTER, p, 300, y, 30);
y = y - 70;
}
}
}