I have some code used to watermark PDFs using iTextSharp. The code works fine for most PDFs, but there has been one test case where the watermark is not visible on a PDF of a scanned document. (I have other scanned documents where it does appear though).
I am using the GetOverContent() method.
This is my code for adding the watermark;
using (PdfReader reader = new PdfReader(this.inputFilename))
{
// Set transparent - 1
PdfGState gstate = new PdfGState();
gstate.FillOpacity = 0.4f;
gstate.StrokeOpacity = 0.5f;
// 2
BaseFont baseFont = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, Encoding.ASCII.EncodingName, false);
using (var stream = new MemoryStream())
{
var pdfStamper = new PdfStamper(reader, stream);
// Must start at 1 because 0 is not an actual page.
for (int i = 1; i <= reader.NumberOfPages; i++)
{
Rectangle pageSize = reader.GetPageSizeWithRotation(i);
// Gets the content ABOVE the PDF, Another option is GetUnderContent(...)
// which will place the text below the PDF content.
PdfContentByte pdfPageContents = pdfStamper.GetOverContent(i);
pdfPageContents.BeginText(); // Start working with text.
// 1
pdfPageContents.SaveState();
pdfPageContents.SetGState(gstate);
float hypotenuse = (float)Math.Sqrt(Math.Pow(pageSize.Width, 2) + Math.Pow(pageSize.Height, 2));
float glyphWidth = baseFont.GetWidth("My watermark text");
float fontSize = 1000 * (hypotenuse * 0.8f) / glyphWidth;
float angle = (float)(Math.Atan(pageSize.Height / pageSize.Width) * (180 / Math.PI));
// Create a font to work with
pdfPageContents.SetFontAndSize(baseFont, fontSize);
pdfPageContents.SetRGBColorFill(128, 128, 128); // Sets the color of the font, GRAY in this instance
// Note: The x,y of the Pdf Matrix is from bottom left corner.
// This command tells iTextSharp to write the text at a certain location with a certain angle.
// Again, this will angle the text from bottom left corner to top right corner and it will
// place the text in the middle of the page.
pdfPageContents.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "My watermark text", pageSize.Width / 2, pageSize.Height / 2, angle);
pdfPageContents.EndText(); // Done working with text
pdfPageContents.RestoreState();
}
pdfStamper.FormFlattening = true; // enable this if you want the PDF flattened.
pdfStamper.FreeTextFlattening = true; // enable this if you want the PDF flattened.
pdfStamper.Close(); // Always close the stamper or you'll have a 0 byte stream.
return stream.ToArray();
}
}
Does anyone have any ideas as to why the watermark may not be appearing and what I can try to fix it?
Kind regards.
The code is based on an assumption it even documents as a fact:
// Note: The x,y of the Pdf Matrix is from bottom left corner.
// This command tells iTextSharp to write the text at a certain location with a certain angle.
// Again, this will angle the text from bottom left corner to top right corner and it will
// place the text in the middle of the page.
pdfPageContents.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "My watermark text", pageSize.Width / 2, pageSize.Height / 2, angle);
The assumption that the x,y of the Pdf Matrix is from bottom left corner unfortunately is wrong: While it indeed is very often the case that the origin of the PDF coordinate system (the default user space coordinate system, to be more precise) is in the lower left corner of the page, this is not required, the origin actually can be literally anywhere (within reasonable limits).
Thus, one has to take the lower left coordinates of the Rectangle pageSize into consideration, too.
The OP meanwhile has confirmed:
I had assumed that the bottom left of the page would have co-ordinates of (0,0) but for this document the co-ordinates were (0, 7022).
Related
I have a very simple code that places the png image into PDF
Document pdfDoc = new Document(PageSize.A4, 25, 25, 25, 10);
string pathfile = ConfigurationManager.AppSettings["Temp_SaveLocation"];
string fileName = "SomeName.pdf";
path = pathfile + fileName;
PdfWriter pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream(path, FileMode.Create));
pdfDoc.Open();
Image imghead = Image.GetInstance(templateFolder + "Letterhead.png");
imghead.SetAbsolutePosition(0, 0);
pdfDoc.Add(imghead);
pdfWriter.CloseStream = true;
pdfDoc.Close();
However, no matter what position for the image I set, that image ends up on a very bottom of a document. I even tried negative values for the absolute position. Nevertheless, the image stays on the very bottom of a document. How can I bring an image to a very top?
Thank you very much in advance
I found out that image will be to the top when this code is removed.
imghead.SetAbsolutePosition(0, 0);
I agree with K J. By setting imghead.SetAbsolutePosition(0, 0), you are setting your image to be at (X,Y) co-ordinate which is mostly lower left co-ordinate of the page.
Generally, lower-left corner of the page coincides with the origin of the coordinate system (0, 0). The upper-right corner of the page coincides with the coordinate (595, 842). You either remove or comment this method imghead.SetAbsolutePosition(0, 0) or adjust the Y co-ordinate of it to move the image towards the upper side of the page.
I am trying to get the x and y coordinates from a pdf document on click event. Pdf documents do not have DOM that's why I am overlaying a div on the top of the pdf document to get the coordinates from the div. I am using itextsharp library and trying to stamp a value you the pdf document. Unfortunately the x and y coordinates of the div are in pixels and I need to relate them to the values in the stamper.
The X coordinates that I am getting from the div click event are: 80, 300, 600, 880
and the X coordinates that I need to enter in the stamper are 10, 205,405,600. What ratio do I need to apply to convert the div coordinates to the stamper coordinates.
private static void InsertTextToPdf(string sourceFileName, string newFileName)
{
using (Stream pdfStream = new FileStream(sourceFileName, FileMode.Open))
{
using (Stream newpdfStream = new FileStream(newFileName, FileMode.Create, FileAccess.ReadWrite))
{
PdfReader pdfReader = new PdfReader(pdfStream);
iTextSharp.text.Rectangle pageSize = pdfReader.GetPageSize(1);
Console.Write(pageSize);
PdfStamper pdfStamper = new PdfStamper(pdfReader, newpdfStream);
PdfContentByte pdfContentByte = pdfStamper.GetOverContent(1);
BaseFont baseFont = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1250, BaseFont.NOT_EMBEDDED);
pdfContentByte.SetColorFill(BaseColor.RED);
pdfContentByte.SetFontAndSize(baseFont, 12);
pdfContentByte.BeginText();
pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "Test", 10, 190, 0);
pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "Test", 205, 190, 0);
pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "Test", 405, 190, 0);
pdfContentByte.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "Test", 600, 190, 0);
pdfContentByte.EndText();
pdfStamper.Close();
}
}
}
Trying to get the userUnit - in my case they are null
PdfReader pdfReader = new PdfReader(pdfStream);
iTextSharp.text.Rectangle pageSize = pdfReader.GetPageSize(1);
Console.Write(pageSize);
iTextSharp.text.pdf.PdfDictionary pageDict = pdfReader.GetPageN(1);
iTextSharp.text.pdf.PdfNumber userUnit = pageDict.GetAsNumber(iTextSharp.text.pdf.PdfName.USERUNIT);
Console.Write("\nPageDict " + pageDict);
Console.Write("\nUser Unit " + userUnit);
You are making different assumptions that are wrong.
There is an FAQ on the official iText web site, where you can find questions that were answered before on Stack Overflow.
Measurement unit
The first FAQ entry you have to read is: How to get the UserUnit from a PDF file?
I quote:
FAQ What is the measurement unit in PDF documents? Most of the measurements
in PDFs are expressed in user space units. ISO-32000-1 (section 8.3.2.3) tells us
“the default for the size of the unit in default user space (1/72 inch) is
approximately the same as a point (pt), a unit widely used in the printing
industry. It is not exactly the same; there is no universal definition of a point.”
In short, 1 in. = 25.4 mm = 72 user units (which roughly corresponds to 72 pt).
By default 72 user units are 1 inch, but this default can be changed by defining a UserUnit.
Coordinate system: orientation
The second FAQ entry you have to read is: How should I interpret the coordinates of a rectangle in PDF?
Let me just copy/paste the image:
Coordinate system: origin
Finally, you have to read: Where is the origin (x,y) of a PDF page?
After reading the previous question, you might assume that the lower-left corner corresponds with the coordinate (0, 0), but that's not always true.
Why your question can't be answered
You claim that you are presenting the PDF as an image, and that you can retrieve coordinates as pixel coordinates, e.g. the 80th pixel in row 300 of all the pixel rows. However, if no one knows at which resolution the PDF is rendered when you converted the PDF (vector data) to a raster image, no one can tell you how the user units relate to pixels. That's something only you can know.
So please read the answers to the questions listed above carefully, and you'll be able to do the necessary Math.
I am implementing a functionality in which I scan the bottom right corner of input pdfs and extract the text present their.I have about 100 pdfs, which have 2160*3024 dimensions(Crop Box).And I am using the following code:
Rectangle pagesize = reader.GetCropBox(1);
Rectangle rect = new Rectangle((float)(0.903 * pagesize.Width), (float)(0.91 * pagesize.Height), pagesize.Width, pagesize.Height);
ITextExtractionStrategy strategy;
RenderFilter[] filter = { new RegionTextRenderFilter(rect) };
strategy = new FilteredTextRenderListener(new LocationTextExtractionStrategy(), filter);
string capturedText = PdfTextExtractor.GetTextFromPage(reader, 1, strategy);
The problem is that in some cases this code starts to return text which is in the top left corner of the pdf. I checked the rotation of the pages and the pdfs where this codes works has rotation of 90 and the case where it does not work has rotation 270. So,I wrote the code to change the rotation of page to 90.But even after changing the rotation , it is still giving the text in the top left corner rather than bottom right.
I am able to get the bottom right corner text for pdf with 270 rotation using the following code:
Rectangle rect = new Rectangle((float)(0 * pagesize.Width), (float)(0 * pagesize.Height),(float)(pagesize.Width * 0.1),(float)(pagesize.Height * 0.1));
I'm currently successfully adding text to a PDF using iTextSharp's ShowTextAligned method. The method looks like this (C#):
public void ShowTextAligned(
int alignment,
string text,
float x,
float y,
float rotation
)
However, it is unclear where the anchor point is for the text we're making. We provide x and y, but does these correspond to the upper left corner of the text rectangle, the lower left corner, or something else? Also is this impacted by line spacing?
I looked at the documentation at this website, but it isn't very explanatory. See PdfContentByte Class / PdfContentByte Methods / ShowTextAligned Method.
Obviously the anchor point depends on the kind of alignment. It does not make sense to say you right-align if your anchor point is at the left side of the text.
Furthermore, text operations usually align relative to the baseline.
Thus:
For left aligned text the anchor point is the left-most point of the text baseline.
For center aligned text the anchor point is the middle point of the text baseline.
For right aligned text the anchor point is the right-most point of the text baseline.
More visually:
This has been generated using:
[Test]
public void ShowAnchorPoints()
{
Directory.CreateDirectory(#"C:\Temp\test-results\content\");
string dest = #"C:\Temp\test-results\content\showAnchorPoints.pdf";
using (Document document = new Document())
{
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(dest, FileMode.Create, FileAccess.Write));
document.Open();
PdfContentByte canvas = writer.DirectContent;
canvas.MoveTo(300, 100);
canvas.LineTo(300, 700);
canvas.MoveTo(100, 300);
canvas.LineTo(500, 300);
canvas.MoveTo(100, 400);
canvas.LineTo(500, 400);
canvas.MoveTo(100, 500);
canvas.LineTo(500, 500);
canvas.Stroke();
ColumnText.ShowTextAligned(canvas, Element.ALIGN_LEFT, new Phrase("Left aligned"), 300, 500, 0);
ColumnText.ShowTextAligned(canvas, Element.ALIGN_CENTER, new Phrase("Center aligned"), 300, 400, 0);
ColumnText.ShowTextAligned(canvas, Element.ALIGN_RIGHT, new Phrase("Right aligned"), 300, 300, 0);
}
}
In C#, .NET 3.5 with winforms I am working on a print preview control. It seems to work fine, but when I print an A4 page I scanned in at real size on an A3 page, teh scale is out by about 3%...
I am using the following code to compute the actual printable area of the page based on the setting selected by the user and I am wondering if I maybe got those calculations wrong?
public static RectangleF GetPrintArea(PageSettings PageSettings)
{
float[] margins;
RectangleF printArea;
// Get the actual page bounds
printArea = PageSettings.Bounds;
// Calculate the hard margins taking into account page orientation
margins = new float[4];
// Left
margins[0] = !PageSettings.Landscape ? PageSettings.HardMarginX : PageSettings.HardMarginY;
// Top
margins[1] = !PageSettings.Landscape ? PageSettings.HardMarginY : PageSettings.HardMarginX;
// Right
margins[2] = margins[0];
// Bottom
margins[3] = margins[1];
// Calculate the real print margins taking into account teh hard and soft margins
// left
margins[0] = Math.Max(margins[0], PageSettings.Margins.Left);
// Top
margins[1] = Math.Max(margins[1], PageSettings.Margins.Top);
// Right
margins[2] = Math.Max(margins[2], PageSettings.Margins.Right);
// Bottom
margins[3] = Math.Max(margins[3], PageSettings.Margins.Bottom);
return new RectangleF(
new PointF(margins[0], margins[1]),
new SizeF(printArea.Width - (margins[0] + margins[2]), printArea.Height - (margins[1] + margins[3]))
);
}
This should return a rectangle which gives the actual area of the page on printing will happen. I use this rectangle for generating both the preview and the printout.
The code for printing out is as follows:
/// <summary>
/// Draws the image on the printing surface
/// </summary>
/// <param name="Graphics">The graohics object with which to draw</param>
protected virtual void PrintImage(Graphics Graphics)
{
RectangleF imageBoundingBox;
RectangleF visibleImageBoundingBox;
RectangleF visibleImage;
// Offset the visible bounding box location by the position of the print area so as to print right within the margins
Graphics.TranslateTransform(-this.Page.PrintableArea.Left, -this.Page.PrintableArea.Top);
// Calculate the bounding box of the scaled image
imageBoundingBox = new RectangleF(this.Page.PrintAreaOrigin.Add(this.ImagePrintLocation), this.Image.Size.Multiply(this.ImagePrintScale));
// Calculate the position and size of the portion of the image bounding box visible in the viewport
visibleImageBoundingBox = RectangleF.Intersect(imageBoundingBox, this.Page.PrintArea);
// Calculate the portion of the image which corresponds to the visible bounding box
visibleImage = new RectangleF(
new PointF(
imageBoundingBox.X < this.Page.PrintArea.Left ? Math.Min(this.Page.PrintArea.Left - imageBoundingBox.X, imageBoundingBox.Width) : 0,
imageBoundingBox.Y < this.Page.PrintArea.Top ? Math.Min(this.Page.PrintArea.Top - imageBoundingBox.Y, imageBoundingBox.Height) : 0
).Divide(this.ImagePrintScale),
visibleImageBoundingBox.Size.Divide(this.ImagePrintScale)
);
// Draw the image
Graphics.DrawImage(this.Image, visibleImageBoundingBox, visibleImage, GraphicsUnit.Pixel);
}
Where Page is a class which contains the page bounds (PageArea), the printable area PrintableArea and the actual print area (PrintArea). The actual print area is the area which is given by the previous rectangle.
Something must be wrong with this approach, but I can't for the life of me figure out what it is. If anybody can identify what's wrong I would be very grateful...