getImportedPage without annotation - pdf

I want to use PdfStamper to enlarge every page, and copy some pages.
Enlarge every page:
for (int i = 1; i <= n; i++) {
page = reader.getPageN(i);
media = page.getAsArray(PdfName.CROPBOX);
if (media == null) {
media = page.getAsArray(PdfName.MEDIABOX);
}
crop = new PdfArray();
crop.add(new PdfNumber(0));
crop.add(new PdfNumber(0));
float w = media.getAsNumber(2).floatValue();
crop.add(new PdfNumber(media.getAsNumber(2).floatValue() * 2));
crop.add(new PdfNumber(media.getAsNumber(3).floatValue() * 1));
page.put(PdfName.MEDIABOX, crop);
page.put(PdfName.CROPBOX, crop);
stamper.getUnderContent(i).setLiteral(String.format("\nq 1 0 0 1 %.1f 0 cm\nq\n", w / 2));
stamper.getOverContent(i).setLiteral("\nQ\nQ\n");
}
Insert an empty page, and copy content from page 1.
stamper.insertPage(2, stamper.getReader().getPageSize(1))
PdfContentByte canvas = stamper.getOverContent(2);
PdfImportedPage ip = stamper.getImportedPage(reader, 1);
Image ipage = Image.getInstance(ip);
canvas.addImage(ipage);
But I found PdfImportedPage deletes annotations, and after I enlarge a page the annotations are in the wrong position.
How can I handle these annotations, both when copying a page and when enlarging.

Displaced Annotations
after I enlarge a page the annotations are in the wrong position.
The Cause Of Their Displacement
The cause for this is simple: You (attempt to) enlarge the page to the right and then you prepend a shift to the right for the static page content. But you don't move the annotations. Thus, the annotations remain in their original position while the static page content is moved away.
What Needs To Be Done
If you actually want to move the coordinates of the static content, you also have to move all the other coordinate arguments in the PDF which relate to that content. This means in particular annotations (multiple entries), destinations, and structure tree information.
A complete implementation thereof is beyond the scope of a stack overflow answer.
A Simpler Option
You (attempt to) enlarge the page to the right and then you prepend a shift to the right for the static page content. Why don't you simply enlarge the page half as far both to the left and the right and leave the content at its coordinates? Then you do not need to change any coordinates at all...
Vanished Annotations
PdfImportedPage deletes annotations
The Cause Of Their Vanishment
The getImportedPage implementations of both the basic PdfWriter and the PdfStamper merely copy the static page content, not the annotations or other extra s. The reason is that in these cases the page is imported as a form Xobject which can be drawn in the content of any content stream (of a page, of an annotation, of another Xobject, ...) but which simply cannot have annotations of its own.
What Can Be Done
You can either flatten all annotations in a preparation step (using a PdfStamper and calling setAnnotationFlattening and setFormFlattening) or duplicate the page in a preparation step (using PdfCopy and its getImportedPage).
The former option is keeping document level information but makes the dynamic annotation contents static. The latter option looses document level information but keeps annotations dynamic.

Related

Increase left margin of an existing pdf using iTextSharp [duplicate]

My web application signs PDF documents. I would like to let users download the original PDF document (not signed) but adding an image and the signers in the left margin of the pdf document.
I've seen this idea in another web application, and I would like to do the same. Of course I would like to do it using itext library.
I have attached two images, the original PDF document (not signed) and the modified PDF document.
First this: it is important to change the document before you digitally sign it. Once digitally signed, these changes will break the signature.
I will break up the question in two parts and I'll skip the part about the actual watermarking as this is already explained here: How to watermark PDFs using text or images?
This question is not a duplicate of that question, because of the extra requirement to add an extra margin to the right.
Take a look at the primes.pdf document. This is the source file we are going to use in the AddExtraMargin example with the following result: primes_extra_margin.pdf. As you can see, a half an inch margin was added to the left of each page.
This is how it's done:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
int n = reader.getNumberOfPages();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
// properties
PdfContentByte over;
PdfDictionary pageDict;
PdfArray mediabox;
float llx, lly, ury;
// loop over every page
for (int i = 1; i <= n; i++) {
pageDict = reader.getPageN(i);
mediabox = pageDict.getAsArray(PdfName.MEDIABOX);
llx = mediabox.getAsNumber(0).floatValue();
lly = mediabox.getAsNumber(1).floatValue();
ury = mediabox.getAsNumber(3).floatValue();
mediabox.set(0, new PdfNumber(llx - 36));
over = stamper.getOverContent(i);
over.saveState();
over.setColorFill(new GrayColor(0.5f));
over.rectangle(llx - 36, lly, 36, ury - llx);
over.fill();
over.restoreState();
}
stamper.close();
reader.close();
}
The PdfDictionary we get with the getPageN() method is called the page dictionary. It has plenty of information about a specific page in the PDF. We are only looking at one entry: the /MediaBox. This is only a proof of concept. If you want to write a more robust application, you should also look at the /CropBox and the /Rotate entry. Incidentally, I know that these entries don't exist in primes.pdf, so I am omitting them here.
The media box of a page is an array with four values that represent a rectangle defined by the coordinates of its lower-left and upper-right corner (usually, I refer to them as llx, lly, urx and ury).
In my code sample, I change the value of llx by subtracting 36 user units. If you compare the page size of both PDFs, you'll see that we've added half an inch.
We also use these coordinates to draw a rectangle that covers the extra half inch. Now switch to the other watermark examples to find out how to add text or other content to each page.
Update:
if you need to scale down the existing pages, please read Fix the orientation of a PDF in order to scale it

Splitting at a specific point in PDFBox

I would like to split to generate a new pdf by concatenating certain individual pages, but the last page has to be split at a certain point (i.e. all content above a limit to be included and everything below to be excluded - I only care about the ones having their upper left corner above a line). Is that possible using PDFbox?
One way to achieve the task, i.e. to split a page at a certain point (i.e. all content above a limit to be included and everything below to be excluded) would be to prepend a clip path.
You can use this method:
void clipPage(PDDocument document, PDPage page, BoundingBox clipBox) throws IOException
{
PDPageContentStream pageContentStream = new PDPageContentStream(document, page, true, false);
pageContentStream.addRect(clipBox.getLowerLeftX(), clipBox.getLowerLeftY(), clipBox.getWidth(), clipBox.getHeight());
pageContentStream.clipPath(PathIterator.WIND_NON_ZERO);
pageContentStream.close();
COSArray newContents = new COSArray();
COSStreamArray contents = (COSStreamArray) page.getContents().getStream();
newContents.add(contents.get(contents.getStreamCount()-1));
for (int i = 0; i < contents.getStreamCount()-1; i++)
{
newContents.add(contents.get(i));
}
page.setContents(new PDStream(new COSStreamArray(newContents)));
}
to clip the given page along the given clipBox. (It first creates a new content stream defining the clip path and then arranges this stream to be the first one of the page.)
E.g. to clip the content of a page along the horizontal line 650 units above the bottom, do this:
PDPage page = ...
PDRectangle cropBox = page.findCropBox();
clipPage(document, page, new BoundingBox(
cropBox.getLowerLeftX(),
cropBox.getLowerLeftY() + 650,
cropBox.getUpperRightX(),
cropBox.getUpperRightY()));
For a running example look here: ClipPage.java.

iTextSharp rotated PDF page reverts orientation when file is rasterized at print house

Using iTextSharp I am creating a PDF composed of a collection of existing PDFs, some of the included PDFs are landscape orientation and need to be rotated. So, I do the following:
private static void AdjustRotationIfNeeded(PdfImportedPage pdfImportedPage, PdfReader reader, int documentPage)
{
float width = pdfImportedPage.Width;
float height = pdfImportedPage.Height;
if (pdfImportedPage.Rotation != 0)
{
PdfDictionary pageDict = reader.GetPageN(documentPage);
pageDict.Put(PdfName.ROTATE, new PdfNumber(0));
}
if (width > height)
{
PdfDictionary pageDict = reader.GetPageN(documentPage);
pageDict.Put(PdfName.ROTATE, new PdfNumber(270));
}
}
This works great. The included PDFs rotated to portrait orientation if needed. The PDF prints correctly on my local printer.
This file is sent to a fulfillment house, and unfortunately, the landscape included files do not print properly when going through their printer and rasterization process. They use Kodak (Creo) NexRip 11.01 or Kodak (Creo) Prinergy 6.1. machines. The fulfillment house's suggestion is to: "generate a new PDF file after we rotate pages or make any changes to a PDF. It is as easy as exporting out to a PostScript and distilling back to a PDF."
I know iTextSharp doesn't support PostScript. Is there another way iTextSharp can rotate included PDFs to hold the orientation when rasterized?
First let me assure you that changing the rotation in the page dictionary is the correct procedure to achieve what you want. As far as I can see your code, there's nothing wrong with it. You are doing the right thing.
Unfortunately, you are faced with a third party product over which you have no control that is not doing the right thing. How to solve this?
I have written an example called IncorrectExample. I have named it that way because I don't want it to be used in a context that is different from yours. You can safely ignore all the warnings I added: they are not meant for you. This example is very specific to your problem.
Please try the following code:
public void manipulatePdf(String src, String dest)
throws IOException, DocumentException {
// Creating a reader
PdfReader reader = new PdfReader(src);
// step 1
Rectangle pagesize = getPageSize(reader, 1);
Document document = new Document(pagesize);
// step 2
PdfWriter writer
= PdfWriter.getInstance(document, new FileOutputStream(dest));
// step 3
document.open();
// step 4
PdfContentByte cb = writer.getDirectContent();
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
pagesize = getPageSize(reader, i);
document.setPageSize(pagesize);
document.newPage();
PdfImportedPage page = writer.getImportedPage(reader, i);
if (isPortrait(reader, i)) {
cb.addTemplate(page, 0, 0);
}
else {
cb.addTemplate(page, 0, 1, -1, 0, pagesize.getWidth(), 0);
}
}
// step 4
document.close();
reader.close();
}
public Rectangle getPageSize(PdfReader reader, int pagenumber) {
Rectangle pagesize = reader.getPageSizeWithRotation(pagenumber);
return new Rectangle(
Math.min(pagesize.getWidth(), pagesize.getHeight()),
Math.max(pagesize.getWidth(), pagesize.getHeight()));
}
public boolean isPortrait(PdfReader reader, int pagenumber) {
Rectangle pagesize = reader.getPageSize(pagenumber);
return pagesize.getHeight() > pagesize.getWidth();
}
I have taken the pages.pdf file as an example. This file is special in the sense that it has two pages in landscape that are created in a different way:
one page is a page of which the width is smaller than the height (sounds like it's a page in portrait), but as there's a /Rotate value of 90 added to the page dictionary, it is shown in landscape.
the other page isn't rotated, but it has a height that is smaller than the width.
In my example, I am using the classes Document and PdfWriter to create a copy of the original document. This is wrong in general because it throws away all interaction. I should use PdfStamper or PdfCopy instead, but it is right in your specific case because you don't need the interactivity: the final purpose of the PDF is to be printed.
With Document, I create new pages using a new Rectangle that uses the lowest value of the dimensions of the existing page as the width and the highest value as the height. This way, the page will always be in portrait. Note that I use the method getPageSizeWithRotation() to make sure I get the correct width and height, taking into account any possible rotation.
I then add a PdfImportedPage to the direct content of the writer. I use the isPortrait() method to find out if I need to rotate the page or not. Observe that the isPortrait() method looks at the page size without taking into account the rotation. If we did take into account the rotation, we'd rotate pages that don't need rotating.
The resulting PDF can be found here: pages_changed.pdf
As you can see, some information got lost: there was an annotation on the final page: it's gone. There were specific viewer preferences defined for the original document: they're gone. But that shouldn't matter in your specific case, because all that matters for you is that the pages are printed correctly.

Converting the zoom level of all links in a PDF document

I have a document in PDF format. The document consists of several chapters, sections, etc.
Within the text, there are references to other chapters or sections; for instance:
We will see in chapter 15 that ...
The notion of ..., mentioned in section 7.1, ...
The references are "links"; that is, when you click on them, it jumps to the corresponding text.
However, the links change the zoom level of the PDF to "Fit Page", as shown in the following dialog box (the screenshot is taken in Adobe Acrobat):
I don't like this behavior, and prefer that the zoom level does not change. To this end, there's an option called "Inherit Zoom".
The problem is that there are too many links in the document to change them manually. So, I want to somehow programmatically change the zoom level of all links in the PDF document to "Inherit Zoom".
Is this possible using iText or similar libraries?
You may try Docotic.Pdf Library for this. To accomplish your task following should be done:
Enumerate actions somehow.
Reset action zoom level to 0 (it means that zoom remains unchanged)
Reset action zoom level function is common and may look like this:
private static void resetActionZoom(PdfAction action)
{
PdfGoToAction goToAction = action as PdfGoToAction;
if (goToAction == null)
return;
// process only actions with FitPage zoom level
if (goToAction.View.Zoom != PdfZoom.FitPage)
return;
goToAction.View.SetZoom(0); // now zoom will remain unchanged after click by link
}
Here is a sample that enumerates all actions in PDF document and reset zoom level for each:
PdfDocument pdf = new PdfDocument("path_to_your_file.pdf");
foreach (PdfAction action in pdf.Actions)
resetActionZoom(action);
pdf.Save("UpdateAllActions.pdf");
Another (and more accurate) way is to enumerate all links on each page and update associated actions the same way:
PdfDocument pdf = new PdfDocument("path_to_your_file.pdf");
foreach (PdfPage page in pdf.Pages)
{
foreach (PdfWidget widget in page.Widgets)
{
PdfActionArea actionArea = widget as PdfActionArea;
if (actionArea == null)
continue;
resetActionZoom(actionArea.Action);
}
}
pdf.Save("UpdatePageLinks.pdf");
Foxit reader
Preferences/Page Display
check "Forbid the change of the current Zoom factor during execution of "Go to Destination actions(these actions can be launched from bookmarks)"

Mirrored (Flipped) Printing A PDF File

I generating ID Cards of Students of My College in a PDF file using ASP.NET (Framework 3.5) and Crystal Reports But I Want to print the Cards in a Transparent Sheet and Paste it on a Plastic Card of same size for that i need the everything to be printed mirrored.
I tried designing the crystal reports in mirrored form itself but could not find a way to write text in mirrored form. Can anyone suggest a way to do this work all I want is to Flip the contents in PDF File or in Crystal Report.
A couple of Ideas:
1) Render the PDF to an Image (using Ghostscript/ImageMagick or commercial PDF library( (eg Tif) and mirror the image for printing
2) Mirror the PDF Itself, might be possible with iTextSharp
3) Use the reporting tool and try and use some kind of reverse font (coud be quick option)
Any API that lets you import pages and write directly to the PDF content stream will let you do this.
In iText (Java), it'd look something like this:
PdfReader reader = new PdfReader(pdfPath);
Document doc = new Document();
PdfWriter writer = PdfWriter.getInstance( doc, new FileOutputStream(outPath) );
for (int pageNum = 1; pageNum <= reader.getNumberOfPages(); ++pageNum) {
PdfImportedPage page = writer.getImportedPage(reader, pageNum);
PdfContentByte pageContent = writer.getDirectContent();
// flip around vertical axis
pageContent.addTemplate(page, 1f, 0f, 0f, -1f, page.getWidth(), 0f);
doc.newPage();
}
The above code is making the following ass-u-me-ptions:
The default Document() page size matches the size of the current PdfImportedPage.
The source pages aren't rotated.
There are no annotations, optional content groups (layers), and various other bits that aren't just represented in the page contents.
Some workarounds:
// keep the page size consistent
PdfImportedPage page = writer.getImportedPage(reader, pageNum);
doc.newPage(page.getBoundingBox());
PdfContentByte pageContent = writer.getDirectContent();
pageContent.addTemplate(...);
// to compensate for a page's rotation, you need to either rotate the target page
// Easy in PdfStamper, virtually impossible with `Document` / `PdfWriter`.
AffineTransform unRotate = AffineTranform.getRotateInstance(degToRad(360 - pageRotation), pageCenterX, pageCenterY)
AffineTransform flip = new AffineTransform(1f, 0f, 0f, -1f, page.getWidth(), 0f);
AffineTransform finalTrans = flip;
finalTrans.concatenate(unRotate);
pageContent.addTemplate(page, finalTrans);
FAIR WARNING: My 2d matrix-fu isn't all that strong. I'm almost certainly doing something wrong. Debugging these sorts of things is a real PITA. Stuff either "looks right" or is so badly screwed up its off the page entirely (ergo invisible, so you don't know which way it went). I often change the page rectangles by [-1000 -1000 1000 1000] just so I can see where it all went. Fun stuff.
As for copying annotations and such... ouch. PdfCopy does all that for you, via it's addPage() method, but that doesn't let you transform the page content first. Any changes you make to the PdfImportedPage are ignored. You're really stuck with The Hard Way... manually copying all the fiddly bits and changing them to compensate for your flipped page... or messing with the source to addPage() to get the results you want. Both require some in-depth knowledge of PDF.
Given the specifics, you probably don't need to worry about it, but it's worth mentioning in case someone with a different situation comes along with the same goal.