I am setting a margin for a pdf and checking if the contents of the page are exceeding the margin.
I am easily able to do that if the contents of a page are just text.
Here s what I am doing:
I am using TextMarginFinder. I will set the left margin values of the pdf based on the book size. and check with the finder.getLlx(); since finder.getLlx(); will get me the left most position of a text in that page.
TextMarginFinder finder;
if(leftmar>=finder.getLlx())
{
errormargin=1; //left margin error
System.out.println("Page: "+i+"Margin Error:LeftMArginError ");
}
But this does not work in case if the page contains an image. Although the image goes outside of the margin, I am not getting the error with the above code since the finder.getLlx(); function seems to work only for texts.
Two Questions:
1) While looping through the pages in pdf, if there is an image in that page, how can I check if that particular page contains an image?
2) If it contains an image, how can I obtain its extreme positions?
Update after mkl suggestion
if(leftmar>=finder.getLlx())
{
errormargin=1; //left margin error
System.out.println("finder.getLlx() value ="+finder.getLlx()+", leftmar Value="+leftmar);
}
if(rightmar<= finder.getUrx()){
errormargin=1; //right margin error
System.out.println("finder.getUrx() value ="+finder.getUrx()+", rightmar Value="+rightmar);
}
if(margintop >= finder.getUry()){
errormargin=3; //top margin error
System.out.println("finder.getUry() value ="+finder.getUry()+", margintop Value="+margintop);
}
if(marginbottom >= finder.getLly()){
errormargin=3; //bottom margin error
System.out.println("finder.getLly() value ="+finder.getLly()+", marginbottom Value="+marginbottom);
}
This is more an answer to what the OP actually wanted, a way to retrieve the bounding box of all content on a page.
The OP already uses the iText TextMarginFinder render listener class to determine the bounding box of the text on page. In the context of this answer an analogous class MarginFinder has been developed which does not only consider text but also other kind of content, e.g. bitmap images and vector graphics.
Thus, replacing the use of TextMarginFinder by MarginFinder allows to find the bounding box of any content on the page.
Please be aware:
Any content is considered, the margin finder does not check whether the content makes a difference. E.g. think about white text, white bitmap areas, or white rectangles, all are considered content and, therefore, the bounding box encompasses such invisible content, too. Especially the latter example, white rectangles, might be a problem here or there as some software first paints a white rectangle over the whole page area.
Clipping paths are not considered. Thus, even content that never is drawn (because it is clipped away) makes the bounding box expand.
Page borders are not considered, either. Thus, off-page content like printer marks may make the bounding box expand even more.
The code calculating the bounding box for vector graphics is not correct: it simply returns the bounding box of all control points which in case of Bezier curves may be false. Its ignoring line widths and wedge types also results in somewhat-off coordinates.
Annotations are not considered. Thus, the resulting bounding box may be to small if annotations are expected to also be considered, e.g. for forms.
In spite of these shortcomings, the render listener usually returns correct results. If this is not enough, the class can be extended accordingly.
PS: Anyone who is interested in the original question may find answers in the MarginFinder render listener class and its use.
Related
I have a requirement to add adobe form fields to an existing pdf.
The problem I encounter is when adding fields to a rotated page, the resulting form field text orientation is incorrect.
e.g. A page that is rotated 90 degrees clockwise, results in form field where the text is "vertical".
Is there a workaround to get form fields created with the correct orientation?
The appearance characteristics dictionary (/MK entry) of the widget has an /R entry where the rotation can be set. See e.g. this file.
PDAppearanceCharacteristicsDictionary fieldAppearance
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
fieldAppearance.setRotation(90);
widget.setAppearanceCharacteristics(fieldAppearance);
You may have to adjust your coordinates. To find the best coordinates, use PDFDebugger and hover at the place you want your field to be.
Update:
For checkmarks (and radio buttons) where the appearance stream is created by the user and not by PDFBox (as seen here or in the PDFBox example) you need to set the matrix yourself like this (for 90°):
yesAP.setMatrix(AffineTransform.getQuadrantRotateInstance(1, rect.getWidth(), 0));
The "1" here is for 90°. The translation needs to be adjusted for the other rotations.
From the above image if I want a portion behind the RED Rectangle I can easily get it,
but the issue I cannot get the portion behind the Yellow Rectangle because it is rotated.
So how can I get a portion of an image from a rotated shape on it?
For example my goal is to get a portion of an Image where the rectangle is located on the image. if someone rotates this rectangle by an x degree [in whatever direction] then it is getting difficult to extract the exact portion of an image after applying rotation.
Any suggestions?
Here a more lengthy description of a possible approach. I do not know the Java2D drawing API very well but if I remember correctly it has the capabilities to do what is required.
First you have to figure out the translation and rotation of the subregion you want compared to an equally size rectangle located straight in the upper left corner in the image. Then invert this transformation.
Make a graphics context which is backed by a bitmap in memory. This one should have the size of the subimage you want. Setup the inverse transformation you calculated earlier on the context and draw your image at position 0,0. As Java2D will take the transformation into account you should now get the sub image you want in the memory bitmap.
Mihir, I think you might be getting distracted by the rotation/AffineTransform aspects of this challenge and it is leading you down the wrong road. Also keep in mind that I don't totally know what you mean by "get" here -- do you want to save out the highlighted region to an image? Do you want to render it as a watermark on another image? etc... I'll just try and answer in the general case to get you down the right track.
What you want is the content from the image defined by the polygon in yellow in your image above; ignoring the fact that it looks like a rotated rectangle.
It is late and I am missing a step in here, but I think this will get you 90% of the way there and clarify the last piece (Graphics2D.setClip) that you need.
Create a java.awt.Polygon that defines the region around the area you want.
Use getBounds() or getBounds2D() to get the width/height of the bounding box required to hold this Polygon when rendered out into a rectangle. (e.g. boundingBox)
Create a new BufferedImage with these width/height values.
Get the Graphics2D from the new BufferedImage (e.g. newG2)
newG2.drawImage(originalImage, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height)
NOTE This is where my memory is failing me; at some point you need to set the clip on newG2 (newG2.setClip(someShape)) so when the bounding box is rendered into it, you don't get the full bounding box of graphics rendered in, but instead some subset as defined by the yellow outline.
One easy way to do this is to create two Polygon's:
poly1 = a java.awt.Polygon that defines the yellow selection in the ORIGINAL image.
poly2 = a java.awt.Polygon that defines the exact same shape of Polygon, but shifted to a 0,0 origin point.
poly1 is used to get the bounding box to copy out the full bounding box that encompasses the content selected in yellow (and extra content around it)
poly2 is used to set the clip on the target Graphics2D (newG2) so when the bounding box is rendered into it, we clip back out everything outside of that Yellow shape so we just get the content in Yellow. You'll likely want to use an ARGB image type and set the background of the target image as transparent otherwise you'll get a black fill color.
I think this is the right direction for the clips; I was up to my eyeballs in Java2D for years and years but have been out of it for a while and forget if this will give you exactly what you want or not; you might need to tweak it around, but these are all the tools you need.
How does one fill out a horizontal pdf form with the PDFBox library?
I access my fields and fill them using the supplied example code and it works fine. But, if the pdf page is tilted horizontally the filled out text are still left in the vertical position.
I have tried rotating the page first and then filling the form but the fields seem to be independent. I have also tried formatting the field through the various set methods defined for PDField and PDTextbox but this has no effect either.
Finally, I know that some of the rotation properties are controlled through the PDAnnotation and PDAnnotationWidget but trying to set their PDAppearanceCharacteristics has no effect on the initial text rotation. Rather, a user is required to interact with the field in order for this to take effect.
Thanking in advance,
J3lly
I want to remove the bottom part of each page in the PDF, but not change page size, what is the recommended way to do this in java in PDFBOX? How to remove the footer from each page in PDF?
Is there possibly a way to use PDRectangle to just delete all text/images within it?
snippet of what I tried, using rectangle with setCropBox seems to lose page size, maybe cropBox is not intended for this?
PDRectangle rectangle = new PDRectangle();
rectangle.setUpperRightY(mypage.findCropBox().getUpperRightY());
rectangle.setLowerLeftY(50);
rectangle.setUpperRightX(mypage.findCropBox().getUpperRightX());
rectangle.setLowerLeftX(mypage.findCropBox().getLowerLeftX());
mypage.setCropBox(rectangle);
croppedDoc.addPage(mypage);
croppedDoc.save(filename);
croppedDoc.close();
Closest example in pdfbox cookbook examples I could find is on how to remove entire page, however this is not what I'm looking for, I'd like to just delete few elements from the page:
http://pdfbox.apache.org/userguide/cookbook.html
I'm also a newbie, but take a look at this page, in particular, the description of TrimBox. If there's no TrimBox on the page, it defaults to CropBox, which would cause what you're seeing.
In general, don't expect the PDFBox docs to tell you much of anything about PDF itself - to use PDFBox well I think you need to go elsewhere - AFAIK, mostly just to the PDF specification. I haven't even skimmed it yet, though!
The CropBox is the way to go if you want to remove a portion of a page while keeping a rectangular region visible. If you want the page size to remain the same, you need the MediaBox to remain the same.
From the PDF Spec:
CropBox - rectangle (Optional; inheritable) A rectangle, expressed in default user space units, defining the visible region of default
user space. When the page is displayed or printed, its contents are to
be clipped (cropped) to this rectangle and then imposed on the output
medium in some implementation-defined manner (see Section 10.10.1,
“Page Boundaries”). Default value: the value of MediaBox.
MediaBox - rectangle (Required; inheritable) A rectangle (see Section 3.8.4, “Rectangles”), expressed in default user space units,
defining the boundaries of the physical medium on which the page is
intended to be displayed or printed (see Section 10.10.1, “Page
Boundaries”).
A have seen (faulty) applications and libraries that force the CropBox and the MediaBox to be the same, double check that this is not what is happening on your case.
Also take into account that the coordinates origin (0,0) in PDF is the bottom-left corner, some libraries do the translation to top-left for you, some others not, you may also want to double check this on the library you are using.
My question is similar to this one, but is more specific in scope.
In my card game application, I would like for users to be able to click on words located in a scanned jpeg image. Please see this sample Pokemon trading card.
In this case, the user should be able to hover his mouse over the text "Scratch", upon which a pulsing rectangular border will appear around the text, indicating that it is clickable. The problem is how to detect the border of the text. There will be an array of words KNOWN BEFOREHAND that the user may click on (these will be retrieved from a database on a card-by-card basis). To continue our example, the array in this case will be ["Scratch", "Live Coal"]. Once the user clicks on "Scratch", the application must know via a call-back that "Scratch" was chosen instead of "Live Coal".
I was thinking of using optical character recognition libraries to solve this problem, but the open-source options for this are poor in quality (e.g. GOCR) and/or not well-tested on multiple platforms (e.g. Tesseract). I only care about Windows and Mac compatibility. Am I missing an obvious/simpler solution/algorithm that does not require OCR? I cannot simply hand-code in bounding boxes for each card, as there will be thousands of scanned cards in my database. The user may also upload his own custom card scans with an accompanying array of clickable text.
Text color is not always black. See this panorama of different card and text styles that will be permitted. The black cards have white text, and the third-to-last card (Zekrom) has black text with a white outline.
Solutions in any programming language are appreciated. However, please note that I am looking for open-source algorithms and/or libraries. If there is a solution in Ruby or Java, even better, as my code is primarily in these two languages.
EDIT: I forgot to mention that the order of the words/phrases in the array will be the same as on the card. Thus, the array will be ["Scratch", "Live Coal"] instead of ["Live Coal", "Scratch"]. I am mentioning this because it can potentially simplify the task. Thus, for this example, I can simply look for black pixels (though I have to watch out for the black star in the white circle). However, there will be more difficult cases where there is descriptive text under the attack name in a smaller font (again, see the panorama for examples).
I would just write a program that allows you to visually draw a bounding box around your text for simplicity but could could do this buy detecting differences in pixel color. Since the text is black you could see where the upper-left most black pixel is without large indents and within the bottom half of the card.
When the cursor is stationary, check if there is a black pixel either underneath or to 4 pixels around the cursor. If it is, check the first three consecutive (because there still might be a non-black pixel between the letters) non-black pixels to the left of the cursor, to the right, to the top and at the bottom. If yes, use these locations to draw a square. You can use OpenCV.