In our project we are using iText 5.x version to manipulate PDF files and working to migrate that implementation with PDFBox 2.x version.
There is one add image to pdf page scenario and I have converted that code into PDFBox as good as I could do. :) In existing implementation(iText) they add image in template using PdfTemplate and added that template in annotation using PdfAnnotation class.
I don't know how to do that using PDFBox. Also please check that I migrated existing implementation properly or not as I'm newbie in PDF library using Java.
Add Image to PDF(Using iText):
Document document = null;
PdfReader pdfReader = null;
pdfReader = new PdfReader(SourceFilePath());
//we retrieve the total number of pages and the page size
int total = pdfReader.getNumberOfPages();
Rectangle rectangle = pdfReader.getPageSizeWithRotation(1);
document = new Document(rectangle);
PdfImportedPage page;
PdfCopy.PageStamp stamp;
// step 2: we create a PdfCopy object that listens to the document.
PdfCopy copy = new PdfCopy(document, new FileOutputStream(DestinationFilePath(false));
document.open();
// step 4: adding the content
for (int i = 1; i <= total; i++) {
page = copy.getImportedPage(pdfReader, i);
if(i == 1 || aBarcodeVO.getDisplacementVO().isApplyForAllPages()){
BufferedImage bufferedImage = getImage();
Image img = Image.getInstance(bufferedImage, null);
img.scaleToFit(qrImageSize, qrImageSize);
PdfName imgKey = new PdfName(aBarcodeVO.getImgUniqueId() + i);
Rectangle rectPage = pdfReader.getPageSizeWithRotation(i);
stamp = copy.createPageStamp(page);
PdfImage stream = new PdfImage(img, "", null);
stream.put(imgKey, imgKey);
PdfIndirectObject ref = copy.addToBody(stream);
int rotation = pdfReader.getPageRotation(i);
Rectangle cropBoxRect = pdfReader.getCropBox(i);;
//Explicitly Apply rotation to crop box rectangle.
for (int j = 0; j < (rotation/90); j++) {
cropBoxRect = cropBoxRect.rotate();
}
//added Image in template and template in Annotation and finally annotation is added in PDF through PdfCopy.PageStamp
PdfTemplate template = PdfTemplate.createTemplate(copy, img.getPlainWidth(), img.getPlainHeight());
img.setAbsolutePosition(0, 0);
img.setRotationDegrees(rotation);
img.setDirectReference(ref.getIndirectReference());
template.addImage(img);
Rectangle rect = new Rectangle(rectLlx, rectLly, rectUrx, rectUry);
rect.setBorderWidth(0.5f);
rect.setBorderColor(new BaseColor(0xFF, 0x00, 0x00));
PdfAnnotation annotation = PdfAnnotation.createStamp(copy, rect, null, "AnnotationOnly");
annotation.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, template);
annotation.setFlags(PdfAnnotation.FLAGS_PRINT + PdfAnnotation.FLAGS_LOCKED);
annotation.setRotate(rotation);
PdfName annotKey = getAnnotKey(i);
annotation.put(annotKey, annotKey);
stamp.addAnnotation(annotation);
stamp.alterContents();
}
copy.addPage(page);
}
copy.freeReader(pdfReader);
try {
if (document != null) {
document.close();
}
} catch (Exception e) {
System.out.println("Exception in handleAddBarCode() while closing():document:" + e);
}
try {
if (pdfReader != null) {
pdfReader.close();
}
} catch (Exception e) {
System.out.println("Exception in handleAddBarCode() while closing():pdfReader:" + e);
}
Add Image to PDF(Using PDFBox):
PDDocument pdDocument = PDDocument.load(new File(SourceFilePath()));
int total = pdDocument.getNumberOfPages();
PDPage page = pdDocument.getDocumentCatalog().getPages().get(0);
PDRectangle rectangle = getRotatedMediaBox(page);
PDPage pdPage = new PDPage(rectangle);
PDDocument newDocument = new PDDocument();
for (int i = 0; i < total; i++) {
pdPage = newDocument.importPage(pdDocument.getPage(i));
PDRectangle pageRect = getRotatedMediaBox(pdPage);
int rotation = pdPage.getRotation();
PDRectangle cropBoxRect = page.getCropBox();
//Calculate margin between crop box rectangle and page rectangle.
float[] margins = getCropBoxMargin(pageRect, cropBoxRect, rotation);
if (rotation == 90 || rotation == 270) {
cropBoxRect = new PDRectangle(cropBoxRect.getLowerLeftY(), cropBoxRect.getLowerLeftX(), cropBoxRect.getHeight(),
cropBoxRect.getWidth());
}
BufferedImage bufferedImage = getImage();
PDPageContentStream pageContentStream = new PDPageContentStream(newDocument, pdPage,
PDPageContentStream.AppendMode.APPEND, true);
PDImageXObject image = JPEGFactory.createFromImage(newDocument, bufferedImage);
if (rotation == 90 || rotation == 270) {
Matrix matrix = Matrix.getRotateInstance(Math.toRadians(rotation), 0, 0);
PDRectangle cropBox = pdPage.getCropBox();
float tx = (cropBox.getLowerLeftX() + cropBox.getUpperRightX()) / 2;
float ty = (cropBox.getLowerLeftY() + cropBox.getUpperRightY()) / 2;
Rectangle rectang = cropBox.transform(matrix).getBounds();
float scale = Math.min(cropBox.getWidth() / (float)rectang.getWidth(), cropBox.getHeight() / (float)rectang.getHeight());
pageContentStream.transform(Matrix.getTranslateInstance(tx, ty));
pageContentStream.transform(matrix);
pageContentStream.transform(Matrix.getScaleInstance(scale, scale));
pageContentStream.transform(Matrix.getTranslateInstance(-tx, -ty));
}
pageContentStream.drawImage(image, rectLlx, rectLly, qrImageSize, qrImageSize);
pageContentStream.close();
}
newDocument.save(new File(DestinationFilePath(false)));
newDocument.close();
pdDocument.close();
Please help me on this or at least looking forward for your suggestions for code needs to rectify of PDFBox implementation.
Related
I have a requirement to add image to pdf documents and being able later to remove the image if needed.
I manage to add image to documents with this code :
iText.Kernel.Geom.Rectangle pagesize;
PdfDocument pdfDoc = new PdfDocument(new PdfReader(sourceFile), new PdfWriter(destinationPath));
int n = pdfDoc.GetNumberOfPages();
ImageData img = ImageDataFactory.Create(image_WM_Path);
for (int i = 1; i <= n; i++)
{
pagesize = pdfDoc.GetPage(i).GetPageSizeWithRotation();
float f = pagesize.GetWidth() / (1.5f * img.GetWidth());
float wImage = img.GetWidth() * f;
float hImage = img.GetHeight() * f;
iText.Kernel.Geom.Rectangle rectangle = new iText.Kernel.Geom.Rectangle(pagesize.GetRight() - wImage, pagesize.GetBottom(),wImage, hImage );
PdfStampAnnotation stamp = new PdfStampAnnotation(rectangle);
stamp.SetStampName(new PdfName("MyCustomStamp"));
PdfFormXObject xObj = new PdfFormXObject(new iText.Kernel.Geom.Rectangle(wImage, hImage));
PdfCanvas canvas = new PdfCanvas(xObj, pdfDoc);
canvas.AddImage(img, 0, 0, wImage, false);
stamp.SetNormalAppearance(xObj.GetPdfObject());
pdfDoc.GetPage(i).AddAnnotation(stamp);
}
pdfDoc.Close();
Is it the correct way to add image as annotation ?
Do i really put an id with the SetStampName method ?
I don't manage to retrieve the annotation in the page's PdfDictionary annotation array, the array is null :
PdfArray annotations = pageDict.GetAsArray(PdfName.Annotation);
Thanks for your help
Is there any way that I can detect color pages in a PDF file?
For example I have a PDF file with 5 pages, and the first and last page are in color. How can I detect the color pages? Can iText do it?
Now my solution is to convert PDF to images and then detect images color or black&white, but it takes too long time to do it, I need a fast way.
Convert pdf to image by Adobe Acrobat Code as fellows
public static void ConvertPDF2Image(string pdfInputPath, string imageOutputPath,
string imageName, int startPageNum, int endPageNum, ImageFormat imageFormat, double resolution)
{
Acrobat.CAcroPDDoc pdfDoc = null;
Acrobat.CAcroPDPage pdfPage = null;
Acrobat.CAcroRect pdfRect = null;
Acrobat.CAcroPoint pdfPoint = null;
// Create the document (Can only create the AcroExch.PDDoc object using late-binding)
// Note using VisualBasic helper functions, have to add reference to DLL
pdfDoc = (Acrobat.CAcroPDDoc)Microsoft.VisualBasic.Interaction.CreateObject("AcroExch.PDDoc", "");
// validate parameter
if (!pdfDoc.Open(pdfInputPath)) { throw new FileNotFoundException(); }
if (!Directory.Exists(imageOutputPath)) { Directory.CreateDirectory(imageOutputPath); }
if (startPageNum <= 0) { startPageNum = 1; }
if (endPageNum > pdfDoc.GetNumPages() || endPageNum <= 0) { endPageNum = pdfDoc.GetNumPages(); }
if (startPageNum > endPageNum) { int tempPageNum = startPageNum; startPageNum = endPageNum; endPageNum = startPageNum; }
if (imageFormat == null) { imageFormat = ImageFormat.Jpeg; }
if (resolution <= 0) { resolution = 1; }
// start to convert each page
for (int i = startPageNum; i <= endPageNum; i++)
{
pdfPage = (Acrobat.CAcroPDPage)pdfDoc.AcquirePage(i - 1);
pdfPoint = (Acrobat.CAcroPoint)pdfPage.GetSize();
pdfRect = (Acrobat.CAcroRect)Microsoft.VisualBasic.Interaction.CreateObject("AcroExch.Rect", "");
int imgWidth = (int)((double)pdfPoint.x * resolution);
int imgHeight = (int)((double)pdfPoint.y * resolution);
pdfRect.Left = 0;
pdfRect.right = (short)imgWidth;
pdfRect.Top = 0;
pdfRect.bottom = (short)imgHeight;
// Render to clipboard, scaled by 100 percent (ie. original size)
// Even though we want a smaller image, better for us to scale in .NET
// than Acrobat as it would greek out small text
pdfPage.CopyToClipboard(pdfRect, 0, 0, (short)(100 * resolution));
IDataObject clipboardData = Clipboard.GetDataObject();
if (clipboardData.GetDataPresent(DataFormats.Bitmap))
{
Bitmap pdfBitmap = (Bitmap)clipboardData.GetData(DataFormats.Bitmap);
pdfBitmap.Save(Path.Combine(imageOutputPath, imageName) + i.ToString() + "." + imageFormat.ToString(), imageFormat);
pdfBitmap.Dispose();
}
}
pdfDoc.Close();
Marshal.ReleaseComObject(pdfPage);
Marshal.ReleaseComObject(pdfRect);
Marshal.ReleaseComObject(pdfDoc);
Marshal.ReleaseComObject(pdfPoint);
}
////detect image Color or black and white as fellows
Bitmap box1 = new Bitmap(PictureBox1.Image);
Color c = new Color()
int rr, gg, bb;
for(int i=0;i<PictureBox1.Width;i++){
for(int j=0;j<PictureBox1.Height;j++){
c= box1.GetPixel(i,j);
rr= c.R; gg=c.g;bb=c.B;
if(c ==Color.Black||c= Color.White){
MessageBox.Show("black and white dot")
}
else {
if(rr==gg==bb){
MessageBox.Show("Gray dot");
}
else {
MessageBOx.Show("Color dot");
}
}
}
}
When I tried to write an image on Text Field, the image is written behind text. It is working for normal text and image but not on Form Fields. I want to write image on Text that covers text behind.
void OldMethod()
{
string path = #"C:\Users\";
string OutLocation = path + "New.pdf";
string Old = path + "Old.pdf";
PdfReader reader1 = new PdfReader(Old);
using (FileStream fs = new FileStream(OutLocation, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (PdfStamper stamper = new PdfStamper(reader1, fs))
{
int pageCount1 = reader1.NumberOfPages;
//Create a new layer
for (int i = 1; i <= pageCount1; i++)
{
iTextSharp.text.Rectangle rect = reader1.GetPageSize(i);
PdfContentByte cb = stamper.GetOverContent(i);
AcroFields Fields = stamper.AcroFields;
cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 50);
Fields.SetField("Price", #"1323423345");
Fields.SetFieldProperty("Price", "TEXTCOLOR", BaseColor.RED, null);
string Img = path + "red_slash.jpg";
iTextSharp.text.Image jpeg = Image.GetInstance(System.Drawing.Image.FromFile(Img), ImageFormat.Jpeg);
float width = 100;
float height = 200;
jpeg.ScaleToFit(width, height);
jpeg.SetAbsolutePosition(210, 315);
cb.AddImage(jpeg);
}
}
}
}
Here I need to write the image on the field "Price"
I'm using itextsharp to join mutiple pdf documents and add a footer.
My code works fine - except for landscape pages - it isn't detecting the page rotation - the footer is not centerd for landscape:
public static int AddPagesFromStream(Document document, PdfCopy pdfCopy, Stream m, bool addFooter, int detailPages, string footer, int footerPageNumOffset, int numPages, string pageLangString, string printLangString)
{
CreateFont();
try
{
m.Seek(0, SeekOrigin.Begin);
var reader = new PdfReader(m);
// get page count
var pdfPages = reader.NumberOfPages;
var i = 0;
// add pages
while (i < pdfPages)
{
i++;
// import page with pdfcopy
var page = pdfCopy.GetImportedPage(reader, i);
// get page center
float posX;
float posY;
var rotation = page.BoundingBox.Rotation;
if (rotation == 0 || rotation == 180)
{
posX = page.Width / 2;
posY = 0;
}
else
{
posX = page.Height / 2;
posY = 20f;
}
var ps = pdfCopy.CreatePageStamp(page);
var cb = ps.GetOverContent();
// add footer
cb.SetColorFill(BaseColor.WHITE);
var gs1 = new PdfGState {FillOpacity = 0.8f};
cb.SetGState(gs1);
cb.Rectangle(0, 0, document.PageSize.Width, 46f + posY);
cb.Fill();
// Text
cb.SetColorFill(BaseColor.BLACK);
cb.SetFontAndSize(baseFont, 7);
cb.BeginText();
// create text
var pages = string.Format(pageLangString, i + footerPageNumOffset, numPages);
cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, printLangString, posX, 40f + posY, 0f);
cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, footer, posX, 28f + posY, 0f);
cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, pages, posX, 20f + posY, 0f);
cb.EndText();
ps.AlterContents();
// add page to new pdf
pdfCopy.AddPage(page);
}
// close PdfReader
reader.Close();
// return number of pages
return i;
}
catch (Exception e)
{
Console.WriteLine(e);
return 0;
}
}
How do I detect the page rotation (e.g. landscape) format in this case? The given example works for PdfReader but not for PdfCopy.
Edit:
Why do I need PdfCopy? I tried copying a word pdf export. Some word hyperlinks will not work when you try to copy pages with PdfReader. Only PdfCopy transfers all needed page informations.
Edit: (SOLVED)
You need to use reader.GetPageRotation(i);
You need to use reader.GetPageRotation(i);
Solved code:
public static int AddPagesFromStream(Document document, PdfCopy pdfCopy, Stream m, bool addFooter, int detailPages, string footer, int footerPageNumOffset, int numPages, string pageLangString, string printLangString)
{
CreateFont();
try
{
m.Seek(0, SeekOrigin.Begin);
var reader = new PdfReader(m);
// get page count
var pdfPages = reader.NumberOfPages;
var i = 0;
// add pages
while (i < pdfPages)
{
i++;
// import page with pdfcopy
var page = pdfCopy.GetImportedPage(reader, i);
// get page center
float posX;
float posY;
var rotation = reader.GetPageRotation(i);
if (rotation == 0 || rotation == 180)
{
posX = page.Width / 2;
posY = 0;
}
else
{
posX = page.Height / 2;
posY = 20f;
}
var ps = pdfCopy.CreatePageStamp(page);
var cb = ps.GetOverContent();
// add footer
cb.SetColorFill(BaseColor.WHITE);
var gs1 = new PdfGState {FillOpacity = 0.8f};
cb.SetGState(gs1);
cb.Rectangle(0, 0, document.PageSize.Width, 46f + posY);
cb.Fill();
// Text
cb.SetColorFill(BaseColor.BLACK);
cb.SetFontAndSize(baseFont, 7);
cb.BeginText();
// create text
var pages = string.Format(pageLangString, i + footerPageNumOffset, numPages);
cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, printLangString, posX, 40f + posY, 0f);
cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, footer, posX, 28f + posY, 0f);
cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, pages, posX, 20f + posY, 0f);
cb.EndText();
ps.AlterContents();
// add page to new pdf
pdfCopy.AddPage(page);
}
// close PdfReader
reader.Close();
// return number of pages
return i;
}
catch (Exception e)
{
Console.WriteLine(e);
return 0;
}
}
In your code sample, page is of type PdfImportedPage. You're asking for its Bounding Box. PdfImportedPage is of type PdfTemplate, so you're asking for the Bounding Box of an XObject. I doubt that will work.
You should ask the reader object for the rotation of the page. I guess you've already discovered that yourself because you say "The given example works for PdfReader, but not for PdfCopy".
I have a PDF file which I need to insert an image on the 1st page and then resize the original size from A4 to some other size.
I manage to add an image into the PDF but not resizing it with the following code:
string pdfFile = #"C:\Temp\a.pdf";
PdfReader reader = new PdfReader(pdfFile);
PdfStamper stamper = new PdfStamper(reader, new FileStream(#"C:\Temp\out.pdf", FileMode.Create), PdfWriter.VERSION_1_5);
PdfContentByte writer = stamper.GetOverContent(1);
stamper.FormFlattening = true;
stamper.SetFullCompression();
Image topImage = Image.GetInstance(#"C:\Temp\c.jpg");
topImage.ScalePercent(19f);
topImage.SetAbsolutePosition(142, 700);
writer.AddImage(topImage);
Document myPDF = writer.PdfDocument;
myPDF.SetPageSize(PageSize.A3);
stamper.Close();
stamper = null;
Is there anything wrong or missing in my code?
Below is how a PDF can be resized:
private string PdfResize(string tmpPdfFilename, string resultRootName)
{
try
{
string newPdfFilename = resultRootName + ".pdf";
PdfReader resizeReader = new PdfReader(tmpPdfFilename);
Rectangle newRect = new Rectangle(0, 0, Convert.ToSingle(_pdfNewSizeW), Convert.ToSingle(_pdfNewSizeH));
Document doc = new Document(newRect);
Document.Compress = true;
PdfWriter resizeWriter = PdfWriter.GetInstance(doc, new FileStream(newPdfFilename, FileMode.Create));
doc.Open();
PdfContentByte cb = resizeWriter.DirectContent;
for (int pageNumber = 1; pageNumber <= resizeReader.NumberOfPages; pageNumber++)
{
PdfImportedPage page = resizeWriter.GetImportedPage(resizeReader, pageNumber);
cb.AddTemplate(page, newRect.Width / resizeReader.GetPageSize(pageNumber).Width, 0, 0,
newRect.Height / resizeReader.GetPageSize(pageNumber).Height, 0, 0);
doc.NewPage();
}
doc.Close();
doc = null;
return newPdfFilename;
}
catch (Exception exp)
{
return String.Empty;
}
}
Despite being an older thread the original question & thread was very helpful recently.
Here's another version that processes the Pdf input as a byte array instead of file path (more helpful for web app environment), and handles all IDisposable references...
public static byte[] ResizePdfPageSize(byte[] pdfBytes, Rectangle pageSize)
{
Document.Compress = true;
using (var outputMemoryStream = new MemoryStream())
using (var targetDoc = new Document(pageSize))
using (var pdfReader = new PdfReader(pdfBytes))
using (var pdfWriter = PdfWriter.GetInstance(targetDoc, outputMemoryStream))
{
targetDoc.Open();
PdfContentByte pdfContentByte = pdfWriter.DirectContent;
var pageCount = pdfReader.NumberOfPages;
for (int pageNumber = 1; pageNumber <= pageCount; pageNumber++)
{
PdfImportedPage page = pdfWriter.GetImportedPage(pdfReader, pageNumber);
var currentPageSize = pdfReader.GetPageSize(pageNumber);
var scaledPageWidth = pageSize.Width / currentPageSize.Width;
var scaledPageHeight = pageSize.Height / currentPageSize.Height;
pdfContentByte.AddTemplate(
page,
scaledPageWidth,
0, 0,
scaledPageHeight,
0, 0
);
//Move document cursor to next Page!
targetDoc.NewPage();
}
targetDoc.Close();
byte[] finalFileBytes = outputMemoryStream.ToArray();
return finalFileBytes;
}
}
Usage with pre-set PageSizes from iTextSharp is very handy:
var originalPdfBytes = File.ReadAllBytes(fileInfo.FullName);
var resizedBytes = PdfHelper.ResizePdfPageSize(originalPdfBytes, PageSize.A4);
File.WriteAllBytes(fileName, resizedBytes);
EDIT: The above method answers the core question and got me where I needed to get for my issues. However, after working on this and ironing out numerous issues and shortcomings of the above simplified method, I've now put the whole helpful code base on github to share for anyone else interested. The github PdfHelpers project now handle aspect ratio better, page margins, rotation of content for better scaling as landscape, content that's already rotated (in my limited test cases), etc. while also providing other helpful code for simple Pdf tasks.
https://github.com/cajuncoding/PdfHelpers