Using iText 7.1.9 java edition, I am attempting to add an image to a PDF at a fixed/absolute location, if the PDF is not landscape then I rotate it 90 degrees, however, once the page is rotated the origin point (was bottom left corner) also rotates (now at the top left corner and rotated 90 degrees), so when I insert the image it ends up rotated and uses the wrong origin point. See the two example PDF linked below for a visual of what is happening.
Is there any way to change a page origin point to the bottom left corner after a page is rotated? Is there a better way to rotate a page than using PdfDocument.getPage(p).setRotation?
Should I simply rotate the image and do some math to work out the new location for any additional elements? I have attempted rotating the image using imageData.setRotation(90) but it appears to do nothing.
The following are the inputs using the PDF attached at the bottom of this question:
//String pdfPath = "before_expected.pdf";
//String pdfDest = "after_expected.pdf";
//Or
String pdfPath = "before_unexpected.pdf";
String pdfDest = "after_unexpected.pdf";
The following is my code to rotate pages and add the image:
//Open existing PDF
FileInputStream inputStream = new FileInputStream(pdfPath);
PdfReader reader = new PdfReader(inputStream).setUnethicalReading(true);
//Create new PDF
FileOutputStream outputStream = new FileOutputStream(pdfDest);
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(reader, writer);
//Load sample image
ImageData imageData = ImageDataFactory.create("C:/sample_image.png");
Image image = new Image(imageData);
//Get root element of PDF
Document document = new Document(pdfDocument);
//Get orientation
Rectangle pageSize = pdfDocument.getPage(1).getPageSize();
System.out.println("Original rotation " + pdfDocument.getPage(1).getRotation() + System.lineSeparator() + pageSize.toString());
//Rotate 90 if page is not landscape - placeholder
if (pageSize.getHeight() > pageSize.getWidth())
{
pdfDocument.getPage(1).setRotation(pdfDocument.getPage(1).getRotation() + 90);
}
//Find page size
Rectangle currentPageSize = pdfDocument.getPage(1).getPageSizeWithRotation();
//Locate image 40% across page and 20% up page
float absoluteXpos = currentPageSize.getWidth() * 0.4f;
float absoluteYpos = currentPageSize.getHeight() * 0.2f;
System.out.println("Image location from origin: " + absoluteXpos + ", "+absoluteYpos);
//Add image
image.setFixedPosition(absoluteXpos, absoluteYpos);
document.add(image);
//Removed code to close any tidy up
document.close();
The output from the second PDF (before_unexpected.pdf) shows that it is rotated, but as mentioned above, rotating the page further appears to make no difference for adding additional content:
Original rotation 90
Rectangle: 842.0x1191.0
Image location from origin: 336.80002, 238.2
Here are the PDF files I used for testing:
The first two images show expected/desired behaviour, the last two show how the image is inserted in the wrong spot (based on the incorrect origin/rotation).
Click here for the original file for the first pdf
Click here for the processed file for the first pdf
Click here for the original file for the second pdf
Click here for the processed file for the second pdf
First of all, when determining whether the current page is not landscape (if (pageSize.getHeight() > pageSize.getWidth())) you should already use getPageSizeWithRotation() instead of simply getPageSize(). You use this method later on in the code and you should have used it at an earlier point as well.
Secondly, if you want to add some content to the fixed position independent of the page rotation, you can use the following instruction before adding the content:
pdfDocument.getPage(1).setIgnorePageRotationForContent(true);
The complete code:
//Open existing PDF
FileInputStream inputStream = new FileInputStream("C:/in.pdf");
PdfReader reader = new PdfReader(inputStream).setUnethicalReading(true);
//Create new PDF
FileOutputStream outputStream = new FileOutputStream("C:/Users/Alexey/Desktop/exp.pdf");
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(reader, writer);
//Load sample image
ImageData imageData = ImageDataFactory.create("C:/sample_image.png");
Image image = new Image(imageData);
//Get root element of PDF
pdfDocument.getPage(1).setIgnorePageRotationForContent(true);
Document document = new Document(pdfDocument);
//Get orientation
Rectangle pageSize = pdfDocument.getPage(1).getPageSizeWithRotation();
System.out.println("Original rotation " + pdfDocument.getPage(1).getRotation() + System.lineSeparator() + pageSize.toString());
//Rotate 90 if page is not landscape - placeholder
if (pageSize.getHeight() > pageSize.getWidth())
{
pdfDocument.getPage(1).setRotation(pdfDocument.getPage(1).getRotation() + 90);
}
//Find page size
Rectangle currentPageSize = pdfDocument.getPage(1).getPageSizeWithRotation();
//Locate image 40% across page and 20% up page
float absoluteXpos = currentPageSize.getWidth() * 0.4f;
float absoluteYpos = currentPageSize.getHeight() * 0.2f;
System.out.println("Image location from origin: " + absoluteXpos + ", "+absoluteYpos);
//Add image
image.setFixedPosition(absoluteXpos, absoluteYpos);
document.add(image);
//Removed code to close any tidy up
document.close();
It gives me same result for both input files:
Related
I want to print a DHL label using a label printer.
The DHL label consists of 2 parts: The left half of the image is just info that I can keep to myself. The right part of the PDF is what should actually be printed.
I would therefore like to cut away the left part of the PDF.
I do not want to make it blank, but I really I want to cut if off.
How could I do this?
You essentially want to cut away one half of the PDF page; looking at you screenshot most likely the lower half.
Using iTextSharp 5.5.13.3 you can do that like this:
var testFile = #"new pdf1.pdf";
var resultFile = #"new pdf1-Cut.pdf";
using (PdfReader pdfReader = new PdfReader(testFile))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, File.Create(resultFile)))
{
for (int i = 1; i <= pdfReader.NumberOfPages; i++)
{
Rectangle cropBox = pdfReader.GetCropBox(i);
PdfArray newCropBox = new PdfArray(new float[] {
cropBox.Left, (cropBox.Bottom + cropBox.Top) / 2,
cropBox.Right, cropBox.Top });
PdfDictionary pageDictionary = pdfReader.GetPageN(i);
pageDictionary.Put(PdfName.CROPBOX, newCropBox);
pageDictionary.Put(PdfName.MEDIABOX, newCropBox);
}
}
(CutPages test CutInHalfForTmighty)
Before
After
I need to copy annotations using PdfWriter instead of PdfCopy because at the time of the copy I need to resize/rotate the page. Can anyone tell me how to do this?
You think you need to use a plain PdfWriter instead of a PdfCopy for copying PDFs because you need to resize/rotate the page and iText in Action, 2nd Ed, says doing so is not possible with the PdfCopy class. Thus, you look for a way to copy annotations in such a context.
What you should look for instead is a way to rotate or resize pages and at the same time use PdfCopy nonetheless!
While it is true that the PdfCopy class itself does not allow resizing or rotating pages, you can manipulate a PDF loaded into a PdfReader and resize and/or rotate its pages before using the PdfCopy class. If you then copy the pages from this manipulated PdfReader into a PdfCopy, you get a result with resized or rotated pages (due to the manipulated PdfReader) and all the annotations present (due to the use of a PdfCopy).
E.g. you can resize all the pages in a PdfReader like this:
void resize(PdfReader pdfReader, float width, float height) {
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
boolean switched = pdfReader.getPageRotation(i) % 180 != 0;
float widthHere = switched ? height : width;
float heightHere = switched ? width : height;
Rectangle cropBox = pdfReader.getCropBox(i);
float halfWidthGain = (widthHere - cropBox.getWidth()) / 2;
float halfHeightGain = (heightHere - cropBox.getHeight()) / 2;
Rectangle newCropBox = new Rectangle(cropBox.getLeft() - halfWidthGain, cropBox.getBottom() - halfHeightGain,
cropBox.getRight() + halfWidthGain, cropBox.getTop() + halfHeightGain);
Rectangle mediaBox = pdfReader.getPageSize(i);
Rectangle newMediaBox = new Rectangle(Math.min(newCropBox.getLeft(), mediaBox.getLeft()),
Math.min(newCropBox.getBottom(), mediaBox.getBottom()),
Math.max(newCropBox.getRight(), mediaBox.getRight()),
Math.max(newCropBox.getTop(), mediaBox.getTop()));
PdfDictionary pageDictionary = pdfReader.getPageN(i);
pageDictionary.put(PdfName.MEDIABOX, new PdfArray(new float[] {newMediaBox.getLeft(), newMediaBox.getBottom(),
newMediaBox.getRight(), newMediaBox.getTop()}));
pageDictionary.put(PdfName.CROPBOX, new PdfArray(new float[] {newCropBox.getLeft(), newCropBox.getBottom(),
newCropBox.getRight(), newCropBox.getTop()}));
}
}
(CopyWithResizeRotate helper method)
and you can rotate all the pages in a PdfReader like this:
void rotate(PdfReader pdfReader) {
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
int rotation = pdfReader.getPageRotation(i);
int newRotation = rotation + 90 % 360;
PdfDictionary pageDictionary = pdfReader.getPageN(i);
if (newRotation == 0)
pageDictionary.remove(PdfName.ROTATE);
else
pageDictionary.put(PdfName.ROTATE, new PdfNumber(newRotation));
}
}
(CopyWithResizeRotate helper method)
Using these helpers, you can e.g. create a PDF from the rotated and/or resized pages of some source PDF and copy them like this:
byte[] wildPdf = RETRIEVE_SOURCE_PDF;
PdfReader pdfReaderOriginal = new PdfReader(wildPdf);
PdfReader pdfReaderRotate = new PdfReader(wildPdf);
rotate(pdfReaderRotate);
PdfReader pdfReaderResize = new PdfReader(wildPdf);
resize(pdfReaderResize, PageSize.LETTER.getWidth(), PageSize.LETTER.getHeight());
PdfReader pdfReaderRotateResize = new PdfReader(wildPdf);
rotate(pdfReaderRotateResize);
resize(pdfReaderRotateResize, PageSize.LETTER.getWidth(), PageSize.LETTER.getHeight());
try ( OutputStream os = new FileOutputStream(new File(RESULT_FOLDER, "wild-rotated-resized.pdf"))) {
Document document = new Document();
PdfCopy pdfCopy = new PdfCopy(document, os);
document.open();
pdfCopy.addDocument(pdfReaderOriginal);
pdfCopy.addDocument(pdfReaderRotate);
pdfCopy.addDocument(pdfReaderResize);
pdfCopy.addDocument(pdfReaderRotateResize);
document.close();
}
(CopyWithResizeRotate test method testRotateResizeAndCopy)
The result can look as follows, the first row the original pages (#1 A4, #2 HALFLETTER, #3 A5, #4 A5 rotated, #5 500x700), the second row the rotated ones, the third row the resized ones (to LETTER), and the fourth row the rotated and resized ones (to LETTER). The Adobe Reader thumbnails unfortunately are not at all to scale:
Manipulating a single PDF only
If you actually only want to resize/rotate pages of a single input PDF, you should not use a PdfCopy instance but instead a PdfStamper:
PdfReader pdfReader = new PdfReader(SOURCE);
[...manipulate properties of the pdfReader like above...]
new PdfStamper(pdfReader, TARGET_STREAM).close();
The advantage here is that not only page-level data but also document-level data of the original document are retained.
Special annotations
There is one type of annotations which will behave in an unexpected manner with the code above: annotations with the NoRotate flag set. Such annotations will behave like this when their host page is rotated:
(ISO 32000-2 section 12.5.3 — Annotation flags)
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();
}
I'm using itextSharp to add anotations in a pdf document.
I have a pdf document that already contains an image saved in it, it's a stamp.
So I draw some stroke on this pdf in the stamp and everything is fine when I draw them in my WPF but when I send the pdf by email using iTextSharp for the conversion the line I drawed is now below the stamp.
How I can solve this problem ?
Thank you
The explanation you posted as an answer (BTW, more apropos would have been to edit your question to contain that data) explains the issue.
There are two principal types of objects visible on a PDF page:
the PDF page content;
annotations associated with the page.
The annotations are always displayed above the page content if they are displayed at all.
In your case you add the image to the PDF page content (using OverContent or UnderContent only changes where in relation to other PDF page content material your additions appear). The stamp, on the other hand, most likely is realized by means of an annotation. Thus, the stamp annotation always is above your additions.
If you want to have your additions appear above the stamp, you either have to add your additions as some kind of annotation, too, or you have to flatten the stamp annotation into the page content before adding your stuff.
Which of these varients is better, depends on the requirements you have. Are there any requirements forcing the stamp to remain a stamp annotation? Are there any requirements forcing your additions to remain part of the content? Please elaborate your requirements. As content and annotations have some different properties when displayed or printed, please state all requirements.
And furthermore, please supply sample documents.
So like I said the original pdf have a stamp saved inside it, if I open the pdf with acrobat reader I can move the stamp.
So here my code to write some strokes :
using (var outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read))
using (var intputStream = new FileStream(pathPdf, FileMode.Open, FileAccess.Read, FileShare.Read))
{
PdfReader reader = new PdfReader(intputStream);
using (var pdfStamper = new PdfStamper(reader, outputStream))
{
foreach (var page in pages)
{
if (page != null && page.ExportedImages.HasItems())
{
PdfContentByte pdfContent = pdfStamper.GetOverContent(page.PageIndex);
Rectangle pageSize = reader.GetPageSizeWithRotation(page.PageIndex);
PdfLayer pdfLayer = new PdfLayer(string.Format(ANNOTATIONNAMEWITHPAGENAME, page.PageIndex), pdfContent.PdfWriter);
foreach (ExporterEditPageInfoImage exportedInfo in page.ExportedImages)
{
Image image = PngImage.GetImage(exportedInfo.Path);
image.Layer = pdfLayer;
if (quality == PublishQuality.Normal || quality == PublishQuality.Medium || quality == PublishQuality.High)
{
float width = (float)Math.Ceiling((image.Width / image.DpiX) * 72);
float height = (float)Math.Ceiling((image.Height / image.DpiY) * 72);
image.ScaleAbsolute(width, height);
float x = (float)(exportedInfo.HorizontalTile * (page.TileSize * (72 / 96d)));
float y = (float)Math.Max(0, (pageSize.Height - ((exportedInfo.VerticalTile + 1) * (page.TileSize * (72 / 96d)))));
image.SetAbsolutePosition(x, y);
}
else
throw new NotSupportedException();
pdfContent.AddImage(image);
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
pdfStamper.Close();
}
}
So my strokes are saved good in the pdf the problem the stamp is always on top of everything and I think is normal so can I do a workaround for this ?
Hello I am trying to use the Apache Poi framework to convert each slide of a ppt to an individual png. The problem is that some slides are deformed. For instance there is a slide where the background has a rainbow color to it. And Images that are on some slides do not appear at all on the .png file
here is the code:
FileInputStream is = new FileInputStream(args[0]);
SlideShow ppt = new SlideShow(is);
is.close();
Dimension pgsize = ppt.getPageSize();
Slide[] slide = ppt.getSlides();
for (int i = 0; i < slide.length; i++) {
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height,
BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
//clear the drawing area
graphics.setPaint(Color.white);
graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
//render
slide[i].draw(graphics);
//save the output
FileOutputStream out = new FileOutputStream("C:\\Users\\Farzad\\Desktop\\slide-" + (i+1) + ".png");
javax.imageio.ImageIO.write(img, "png", out);
out.close();
}
For this to work we don't have to use:
graphics.setPaint(Color.white);
Instead use :
graphics.setPaint(
slideShow.getSlides()[0].getBackground().getFill().getForegroundColor()
);