Getting the width of a Chunk does not include the last character - itext

I am rendering 3 characters to the bottom left of a PDF document. I am doing this by creating a Column and then positioning the column accordingly with the correct dimensions to match the Chunk (or in this case, Paragraph)
If you run the following code in LINQPad (or anywhere really) you'll see that the 'Z' character is missing from the output and I can't work out why. The X axis is set to the same width as the width of the chunk.
var pdfDocument = new Document(PageSize.A4);
using (var ms = new MemoryStream())
{
using (pdfDocument)
{
using (var writer = PdfWriter.GetInstance(pdfDocument, ms))
{
pdfDocument.Open();
pdfDocument.NewPage();
var cb = writer.DirectContent;
var font = FontFactory.GetFont("Arial", 16);
var chunk = new Chunk("XYZ", font);
float textWidth = chunk.GetWidthPoint();
var p = new Paragraph(chunk);
var ct = new ColumnText(cb);
ct.SetSimpleColumn(0, 0, textWidth, p.TotalLeading);
ct.AddElement(p);
ct.Go();
pdfDocument.Close();
}
}
var tmpPdfPath = System.IO.Path.GetTempFileName() + ".pdf";
File.WriteAllBytes(tmpPdfPath, ms.ToArray());
Process.Start(tmpPdfPath);
}

Related

Redaction using itext 5 for a scanned pdf

I was trying to redact data from scanned PDF using Itext 5 but somehow it's not working out for me. I was only able to create redaction but not able to apply it. I tried with their given examples but it only works until creating redaction
String fileName = "scanned.pdf";
String dest = "output_scannedPdf.pdf";
PdfReader reader = new PdfReader(SRC + fileName);
int page = 1;
Rectangle rect = new Rectangle(500, 50, 200, 300);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(DEST + dest));
PdfAnnotation annotation = new PdfAnnotation(stamper.getWriter(), rect);
annotation.put(PdfName.SUBTYPE, new PdfName("Redact"));
annotation.setTitle("My author");
annotation.put(new PdfName("Subj"), new PdfName("Redact"));
float[] fillColor = { 0, 0, 0 };
annotation.put(new PdfName("IC"), new PdfArray(fillColor));
float[] fillColorRed = { 1, 0, 0 };
annotation.put(new PdfName("OC"), new PdfArray(fillColorRed));
stamper.addAnnotation(annotation, page);
// manipulatePdf(dest, dest1);
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
I'm getting a null pointer exception in extractLocationsFromRedactAnnots() method PdfName annotSubtype = annotDict.getAsName(PdfName.SUBTYPE);

adding a textbox to the right corner of the existing pdf using ITextSharp in C#

I tiied to add a TextBox to the right corner of the existing pdf using c#, but im unable to get it done. I have wrote the following code,but it is not helping in solving the problem, can any body please suggest me
using (MemoryStream stream = new MemoryStream())
{
PdfReader reader = new PdfReader(bytes);
PdfReader.unethicalreading = true;
Paragraph p = new Paragraph();
Document doc = new Document();
using (PdfStamper stamper = new PdfStamper(reader, stream))
{
PdfContentByte canvas = stamper.GetOverContent(1);
iTextSharp.text.Rectangle size = reader.GetPageSizeWithRotation(1);
//PdfContentByte cb = null;
//PdfImportedPage page;
int pages = reader.NumberOfPages;
for (int i = 1; i <= pages; i++)
{
var size1 = reader.GetPageSize(i);
w = size1.Width;
h = size1.Height;
stamper.FormFlattening = true;
TextField tf = new TextField(stamper.Writer, new iTextSharp.text.Rectangle(0, 0, 300, 100), displaytext);
//Change the orientation of the text
tf.Rotation = 0;
stamper.AddAnnotation(tf.GetTextField(), i);
}
}
bytes = stream.ToArray();
}
File.WriteAllBytes(str, bytes);
As the OP clarified in comments to the question, he wants
to add the text as a page content in the right bottom corner of the page and
the page content previously existing there to be removed.
A simple implementation of this would include
first covering the existing page content with a filled rectangle and
then writing text there.
These tasks can be achieved with these helper methods:
void EmptyTextBoxSimple(PdfStamper stamper, int pageNumber, Rectangle boxArea, BaseColor fillColor)
{
PdfContentByte canvas = stamper.GetOverContent(pageNumber);
canvas.SaveState();
canvas.SetColorFill(fillColor);
canvas.Rectangle(boxArea.Left, boxArea.Bottom, boxArea.Width, boxArea.Height);
canvas.Fill();
canvas.RestoreState();
}
and
ColumnText GenerateTextBox(PdfStamper stamper, int pageNumber, Rectangle boxArea)
{
PdfContentByte canvas = stamper.GetOverContent(pageNumber);
ColumnText columnText = new ColumnText(canvas);
columnText.SetSimpleColumn(boxArea);
return columnText;
}
E.g. like this:
using (PdfReader reader = new PdfReader(source))
using (PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create)))
{
Rectangle cropBox = reader.GetCropBox(1);
Rectangle bottomRight = new Rectangle(cropBox.GetRight(216), cropBox.Bottom, cropBox.Right, cropBox.GetBottom(146));
EmptyTextBoxSimple(stamper, 1, bottomRight, BaseColor.WHITE);
ColumnText columnText = GenerateTextBox(stamper, 1, bottomRight);
columnText.AddText(new Phrase("Some test text to draw into a text box in the lower right corner of the first page"));
columnText.Go();
}
For this source page
the sample code generates this
Addendum
In a comment the OP indicated
it is working for all files but for some pdf files it is displaying in the middle
Eventually he supplied a sample file for which the issue occurs. And indeed, with this file the issue could be reproduced.
The cause for the issue is that the pages in the sample file use page rotation, something that iText (only) partially allows users to ignore. In particular iText automatically rotates text to be upright after rotation and transforms coordinates, but when retrieving the cropbox of a page, one still has to apply rotation before making use of it coordinates. Thus, a more complete example would be like this:
using (PdfReader reader = new PdfReader(source))
using (PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create)))
{
Rectangle cropBox = reader.GetCropBox(1);
int rotation = reader.GetPageRotation(1);
while (rotation > 0)
{
cropBox = cropBox.Rotate();
rotation -= 90;
}
Rectangle bottomRight = new Rectangle(cropBox.GetRight(216), cropBox.Bottom, cropBox.Right, cropBox.GetBottom(146));
EmptyTextBoxSimple(stamper, 1, bottomRight, BaseColor.WHITE);
ColumnText columnText = GenerateTextBox(stamper, 1, bottomRight);
columnText.AddText(new Phrase("Some test text to draw into a text box in the lower right corner of the first page"));
columnText.Go();
}

iTextSharp ColumnText drawing and height not correct

I'm trying to place text into a ColumnText object and calculate its exact height. I'm using iTextSharp 5.5.9
What I'm finding is that there appears to be some "padding" at the top of the ColumnText object and it causes the height calculation to be flawed and the text to be misplaced. I'm trying to understand exactly what's happening - here's my code:
var doc = new Document(PageSize.LETTER, DocumentRenderer.PageMarginSize, DocumentRenderer.PageMarginSize, DocumentRenderer.PageTopMarginSize, DocumentRenderer.PageMarginSize);
doc.SetPageSize(PageSize.LETTER.Rotate());
var writer = PdfWriter.GetInstance(doc, fs);
doc.Open();
var x = 5;
var y = doc.PageSize.Height - 5;
var width = doc.PageSize.Width/ 2;
var height = doc.PageSize.Height - 10;
var lines = new List<string> { "Test string", };// "Test string 2", "Test string 3", "Test string 4" };
for (int i = 0; i < 6; i++)
{
lines.Add("Test String " + i);
}
lines.Add(lines.Aggregate((a, b) => a + ", " + b));
lines.Add(lines.Aggregate((a, b) => a + "\n" + b));
PdfContentByte cb = writer.DirectContent;
cb.SetTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);
cb.SetCMYKColorFill(DocumentRenderer.DarkColor.C, DocumentRenderer.DarkColor.M, DocumentRenderer.DarkColor.Y, DocumentRenderer.DarkColor.K);
Font ListFont = new Font(DocumentRenderer.TextFont, 12);
ColumnText ct = new ColumnText(cb);
ct.Leading = 14;
//ct.UseAscender = true;
ct.Alignment = Element.ALIGN_LEFT;
ct.SetSimpleColumn(x, y - height, x + width, y);
lines.ForEach(line =>
{
ct.AddText(new Phrase(line + "\n", ListFont));
});
ct.Go();
var height1 = (ct.LinesWritten * ct.Leading);
var height2 = y - ct.YLine;
DocumentRenderer.DrawBox(ref doc, ref writer, new BoxInfo(x, y, width, height2), DocumentRenderer.HighlightColor);
if (doc.IsOpen())
doc.Close();
Here are the results - please note that I highlighted the text with my cursor for effect.
I stumbled into this post (iText placement of Phrase within ColumnText) regarding UseAscender and tried it out by uncommenting that line in the code. You can see that on the right in the picture and it didn't appear to work like I was hoping.
What I really want is the text to still have its leading drawn correctly but for that to start at the top of the ColumnText. Also, I'm trying to get an accurate height for the text. The ct.YLine appears to give me the line at the bottom of the text but the text will overhang below that slightly. I'm not sure why that's not correct but I need to know the exact height if that's possible...
Anyone know how I might achieve these two things?

iTextSharp centering text on page

I cannot get the text to be centered on the page. What am I doing wrong? I have tried several ways to get the page with, but none seem to make the text centered on the page.....
BaseFont bf = BaseFont.CreateFont("c:\\windows\\fonts\\calibri.ttf", BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
PdfReader reader = new PdfReader("C:\\temp\\Certificate12.pdf");
var pageSize = reader.GetPageSize(1);
iTextSharp.text.Rectangle rec2 = new iTextSharp.text.Rectangle(PageSize.LETTER);
PdfStamper stamper = new PdfStamper(reader, stream1);
PdfContentByte canvas = stamper.GetUnderContent(1);
canvas.BeginText();
canvas.SetFontAndSize(bf, 24);
string nameText = "First Name Last Name";
int textWidth = (int)nameText.Length;
int canvasWidth = (int)canvas.PdfDocument.PageSize.Width;
float xStart = (canvasWidth / 2) - (textWidth / 2);
canvas.ShowTextAligned(PdfContentByte.ALIGN_CENTER, nameText, xStart, pageSize.GetTop(Utilities.MillimetersToPoints(145)), 0);
First of all, if you use ShowTextAligned with ALIGN_CENTER, iTextSharp will center the text for you, so you do not have to deal with the text width at all. You merely need to tell it to center the text on which center point.
Thus, you can center your text on the page like this:
BaseFont bf = BaseFont.CreateFont("c:\\windows\\fonts\\calibri.ttf", BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
using (PdfReader reader = new PdfReader(source))
using (PdfStamper stamper = new PdfStamper(reader, new FileStream(dest, FileMode.Create, FileAccess.Write)))
{
Rectangle pageSize = reader.GetPageSize(1);
PdfContentByte canvas = stamper.GetUnderContent(1);
string nameText = "First Name Last Name";
canvas.BeginText();
canvas.SetFontAndSize(bf, 24);
canvas.ShowTextAligned(PdfContentByte.ALIGN_CENTER, nameText, (pageSize.Left + pageSize.Right) / 2, pageSize.GetTop(Utilities.MillimetersToPoints(145)), 0);
canvas.EndText();
}

Total page number with itextpdf

I am using itextpdf-5.5.6 for publish my data.
I'm trying to set page numbers in PdfPCell of my pdf using following format : page_num/total_page_num
For this I use PdfTemplate object filling inside total page number before close document.
It warks, but PdfTemplate exceeds PdfPCell border.
Is it possible that all content of cell stay inside of cell properly ?
PdfTemplate pageNumTemplate;
#Test
public void quick_test() throws FileNotFoundException, DocumentException {
String filename = "C:\\test.pdf";
File file = new File(filename);
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
PageNumberEvent pageNumberEvent = new PageNumberEvent();
writer.setPageEvent(pageNumberEvent);
document.open();
for(int i = 0; i < 10000; i++){
document.add(new Paragraph("This is my paragraph"));
document.newPage();
}
document.close();
}
public class PageNumberEvent extends PdfPageEventHelper {
#Override
public void onEndPage(PdfWriter writer, Document document) {
if(pageNumTemplate == null){
pageNumTemplate = writer.getDirectContent().createTemplate(0.1f, 0.1f);
}
PdfPTable table = new PdfPTable(1);
table.setSpacingBefore(50);
Paragraph para = new Paragraph();
Chunk pageNum = new Chunk(writer.getPageNumber() + "/");
para.add(pageNum);
Image totalPageNumImg = null;
try {
totalPageNumImg = Image.getInstance(pageNumTemplate);
} catch (BadElementException e) {
e.printStackTrace();
}
Chunk totalPageNumImgChunk = new Chunk(totalPageNumImg, 0, -1, true);
para.add(totalPageNumImgChunk);
para.setIndentationLeft(370);
PdfPCell cell = new PdfPCell(para);
cell.addElement(para);
table.addCell(cell);
try {
document.add(table);
} catch (DocumentException e) {
e.printStackTrace();
}
}
#Override
public void onCloseDocument(PdfWriter writer,Document document) {
String totalPageNumString = String.valueOf(writer.getPageNumber() - 1);
float widthPoint = totalPageNumString.length() * 10;
float heightPoint = totalPageNumString.length() * 20;
Phrase totalPageNumPhrase = new Phrase(totalPageNumString);
Rectangle templRect = pageNumTemplate.getBoundingBox();
Rectangle rectangle = new Rectangle(templRect.getLeft(), templRect.getBottom(), widthPoint, heightPoint + 2);
pageNumTemplate.setBoundingBox(rectangle);
ColumnText.showTextAligned(pageNumTemplate, Element.ALIGN_LEFT, totalPageNumPhrase, 0, 1, 0);
}
}
Early, in the first onEndPage call, you create a minute template (0.1x0.1) to start with
pageNumTemplate = writer.getDirectContent().createTemplate(0.1f, 0.1f);
which fits into your cell. At the end though, in onCloseDocument, after everything has been layout'ed to a fixed position, you resize this template which makes it grow to the left:
Rectangle templRect = pageNumTemplate.getBoundingBox();
Rectangle rectangle = new Rectangle(templRect.getLeft(), templRect.getBottom(), widthPoint, heightPoint + 2);
pageNumTemplate.setBoundingBox(rectangle);
If you want your template to remain in your table cell, you have to initialize it with a width which most likely will be enough to hold the number of pages, e.g.
pageNumTemplate = writer.getDirectContent().createTemplate(30f, 0.1f);
Here you may have to play around a bit...
As the OP indicated in a comment, not only keeping all content of cell stay inside of cell is required but the content also is expected to be right aligned in the cell.
This second requirement obviously cannot be fulfilled by the OP's code which draws the number of pages into the template using ALIGN_LEFT. Furthermore it does not mix and match with resizing the template on the right size because it has already been positioned on the pages.
To fulfill it, therefore, the OP should
horizontally initialize the template with a size large enough for any expected number of pages (onEndPage);
fill the table cell with right alignment (instead of some pseudo right alignment by means of setIndentationLeft) (onEndPage);
leave the horizontal size of the template as is (onCloseDocument); and
show the number of pages right aligned to the right template border (onCloseDocument).