Firstly, I am trying to make a simple game in Java. I have a viewport that shows a tile map and I have a tank in the middle that moves by controlling the JScrollBars of the scrollpane in which the viewport resides in. So far everything has been going well, until I needed to rotate an image. Here is a picture of the game: Note: the tank body and tilemap are on seperate panels and do not share the same graphics.
Picture of non rotated tank body:
Essentially, I want to rotate a buffered image around its center (rotating in place) using arrow keys. I already have the code for the keys, and I also have a method to try and rotate the buffered image given a buffered image and angle in degrees (the angle is changed to radians in the method). This method will return a buffered image that is rotated correctly. Here is the code:
public static BufferedImage rotateImage(BufferedImage image, double angle) {
if(angle == 0)
return image;
else {
angle = Math.toRadians(angle);
double x = Math.abs(Math.cos(angle));
double y = Math.abs(Math.sin(angle));
int newWidth = (int) Math.floor(image.getWidth()*x + image.getHeight()*y);
int newHeight = (int) Math.floor(image.getHeight()*x + image.getWidth()*y);
BufferedImage rotated = new BufferedImage(newWidth, newHeight, image.getType());
Graphics2D tool = rotated.createGraphics();
AffineTransform transformer = new AffineTransform();
transformer.rotate(angle, image.getWidth()/2, image.getHeight()/2);
tool.drawImage(image, transformer, null);
tool.dispose();
return rotated;
}
}
However, as the title suggests, the image gets cut off at the top and left sides of the image when rotated as shown:
Picture of rotated tank body:
So I have looked at many different forums but I could not solve my problem. I could add whitespace around the image, but that interferes a lot with collision detection which I plan to do later on. I know that it has to do something with the original display being smaller than the display of the rotated image, and I have tried to translate accordingly in many ways. If I translate with this line of code specifically,
transformer.translate((newWidth - image.getWidth())/2, (newHeight - image.getHeight())/2);
Then the image (tank body) rotates without cutting, but bounces out of place as shown (I drew a rectangle to show where it was):
Picture of rotated tank with translation:
I also have tried negating the translations too but it only avails to funky movements.
So, I really have no clue how to solve this, and I have been spending too much time on this problem. I would really appreciate a helpful answer that directly edits my method if possible.
Answer
So here is the opening idea that I needed to realize to answer this problem.
The method to translate and rotate is meant so that the image is not cut off. However, it won't be around the center as intended as seen in the 3rd picture. But again, the method is not intended to recenter it. The painting code itself needs to account for this shift. I simply added variables to account for this:
xOffset = (newWidth - image.getWidth())/2;
yOffset = (newHeight - image.getHeight())/2
And simply subtracted these from where I was painting the tank's body.
Thanks to #camickr for the solution
When rotating a square sprite around the center point, the target image should be larger than the original image by a factor of the square root of 2 (approx. 1.41). For example, a sprite will not be clipped at a rotation angle of 45 °.
I hope this information helps you to solve your problem.
I am trying to get the Absolute position of a PDF field and My code is as follows.
float[] _advisor = reader.getAcroFields().getFieldPositions("_advisor");
float[] _test = reader.getAcroFields().getFieldPositions("_test");
float[] _owner = reader.getAcroFields().getFieldPositions("_owner");
All the fields are vertically aligned same left position.
The problem is the first two fields are on the same page of the PDF and the value of xLeft is same but the Last field _owner is on the second page and the Value of xLeft is off by a big amount. Do i need to subtract an offset or something for pages in different page?
Some things to consider:
The default coordinate used by iText has its origin at the lower left corner of the page.
iText will return coordinates in points, rather than in pixels.
You can display a ruler and grid overlay using Adobe Reader, this enables you to easily gauge where each component is at. Check whether these readings are the same as the values iText provides you with.
If you still think iText is giving you the wrong values, please provide us access to your pdf, and provide us with the values you expect to receive (and why).
One possible issue could be that your mediabox has a different positioning than 0,0. I needed that once so I "normalized" the values like this:
PdfDictionary pageDict = reader.getPageN(pageNumber);
PdfArray mediaBox = (PdfArray)PdfReader.getPdfObject(pageDict.get(PdfName.MEDIABOX));
//check whether the mediabox has a different positioning than 0,0
if(((PdfNumber)mediaBox.getPdfObject(0)).floatValue()!=0){
//normalize X coordinates
lowerLeftX = lowerLeftX-(PdfNumber)mediaBox.getPdfObject(0)).floatValue();
upperRightX = upperRightX-((PdfNumber)mediaBox.getPdfObject(0)).floatValue();
}
if(((PdfNumber)mediaBox.getPdfObject(1)).floatValue()!=0){
//normalize Y coordinates
lowerLeftY = lowerLeftY-((PdfNumber)mediaBox.getPdfObject(1)).floatValue();
upperRightY = upperRightY-((PdfNumber)mediaBox.getPdfObject(1)).floatValue();
}
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.
The Situation:
In PDFBox, PDRectangle objects' default origin (0,0) seems to be the lower-left corner of a page.
For example, the following code gives you a square at the lower-left corner of a page, and each side is 100 units long.
PDRectangle rectangle = new PDRectangle(0, 0, 100, 100);
The Question:
Is it possible to change the origin to the UPPER-LEFT corner, so that, for example, the code above will give you the same square at the UPPER-LEFT corner of the page instead?
The reason I ask:
I was using PDFTextStripper to get the coordinates of the text (by using the getX() and getY() methods of the extracted TextPosition objects). The coordinates retrieved from TextPosition objects seem have an origin (0,0) at the UPPER-LEFT CORNER. I want the coordinates of my PDRectangle objects have the same origin as the coordinates of my TextPosition objects.
I have tried to adjust the Y-coordinates of my PDRectangle by "page height minus Y-coordinate". This gives me the desired result, but it's not elegant. I want an elegant solution.
Note:
Someone has asked a similar question. The answer is what I tried, which is not the most elegant.
how to change the coordiantes of a text in a pdf page from lower left to upper left
You can change coordinate systems somewhat but most likely things won't get more elegant in the end.
To start with...
First of all let's clear up some misconception:
You assume
In PDFBox, PDRectangle objects' default origin (0,0) seems to be the lower-left corner of a page.
This is not true for all cases, merely often.
The area containing the displayed page area (on paper or on screen) usually is defined by the CropBox entry of the page in question:
CropBox rectangle (Optional; inheritable) A rectangle, expressed in default user space units, that shall define the visible region of default user space.
When the page is displayed or printed, its contents shall be clipped (cropped) to this rectangle and then shall be imposed on the output medium in some implementation-defined manner.
... The positive x axis extends horizontally to the right and the positive y axis vertically upward, as in standard mathematical practice (subject to alteration by the Rotate entry in the page dictionary).
... In PostScript, the origin of default user space always corresponds to the lower-left corner of the output medium. While this convention is common in PDF documents as well, it is not required; the page dictionary’s CropBox entry can specify any rectangle of default user space to be made visible on the medium.
Thus, the origin (0,0) can literally be anywhere, it may be at the lower left, at the upper left, in the middle of the page or even far outside the displayed page area.
And by means of the Rotate entry, that area can even be rotated (by 90°, 180°, or 270°).
Putting the origin (as you seem to have observed) in the lower left merely is done by convention.
Furthermore you seem to think that the coordinate system is constant. This also is not the case, there are operations by which you can transform the user space coordinate system drastically, you can translate, rotate, mirror, skew, and/or scale it!
Thus, even if at the beginning the coordinate system is the usual one, origin in lower left, x-axis going right, y-axis going up, it may be changed to something weird some way into the page content description. Drawing your rectangle new PDRectangle(0, 0, 100, 100) there might produce some rhomboid form just right of the page center.
What you can do...
As you see coordinates in PDF user space are a very dynamic matter. what you can do to tame the situation, depends on the context you use your rectangle in.
Unfortunately you were quite vague in the description of what you do. Thus, this will be somewhat vague, too.
Coordinates in the page content
If you want to draw some rectangle on an existing page, you first of all need a page content stream to write to, i.e. a PDPageContentStream instance, and it should be prepared in a manner guaranteeing that the original user space coordinate system has not been disturbed. You get such an instance by using the constructor with three boolean arguments setting all them to true:
PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true, true);
Then you can apply a transformation to the coordinate system. You want the top left to be the origin and the y-value increasing downwards. If the crop box of the page tells you the top left has coordinates (xtl, ytl), therefore, you apply
contentStream.concatenate2CTM(new AffineTransform(1, 0, 0, -1, xtl, ytl));
and from here on you have a coordinate system you wanted, origin top left and y coordinates mirrored.
Be aware of one thing, though: If you are going to draw text, too, not only the text insertion point y coordinate is mirrored but also the text itself unless you counteract that by adding an also mirroring text matrix! If you want to add much text, therefore, this may not be as elegant as you want.
Coordinates for annotations
If you don't want to use the rectangle in the content stream but instead for adding annotations, you are not subject to the transformations mentioned above but you can not make use of it, either.
Thus, in this context you have to take the crop box as it is and transform your rectangle accordingly.
Why PDFBox text extraction coordinates are as they are
Essentially for putting lines of text together in the right order and sorting the lines correctly, you don't want such a weird situation but instead a simple stable coordinate system. Some PDFBox developers chose the top-left-origin, y-increasing-downwards variant for that, and so the TextPosition coordinates have been normalized to that scheme.
In my opinion a better choice would have been to use the default user space coordinates for easier re-use of the coordinates. You might, therefore, want to try working with textPosition.getTextMatrix().getTranslateX(), textPosition.getTextMatrix().getTranslateY() for a TextPosition textPosition
The following seems to be the best way to "adjust" the TextPosition coordinates:
x_adjusted = x_original + page.findCropBox().getLowerLeftX();
y_adjusted = -y_original + page.findCropBox().getUpperRightY();
where page is the PDPage on which the TextPosition object is located
The accepted answer created some problems for me. Also, text being mirrored and adjusting for that just didn't seem like the right solution for me. So here's what I came up with and so far, this has worked pretty smoothly.
Solution (example available below):
Call the getAdjustedPoints(...) method with your original points as you are drawing on paper where x=0 and y=0 is top left corner.
This method will return float array (length 4) that can be used to draw rect
Array order is x, y, width and height. Just pass that addRect(...) method
private float[] getAdjustedPoints(PDPage page, float x, float y, float width, float height) {
float resizedWidth = getSizeFromInches(width);
float resizedHeight = getSizeFromInches(height);
return new float[] {
getAdjustedX(page, getSizeFromInches(x)),
getAdjustedY(page, getSizeFromInches(y)) - resizedHeight,
resizedWidth, resizedHeight
};
}
private float getSizeFromInches(float inches) {
// 72 is POINTS_PER_INCH - it's defined in the PDRectangle class
return inches * 72f;
}
private float getAdjustedX(PDPage page, float x) {
return x + page.getCropBox().getLowerLeftX();
}
private float getAdjustedY(PDPage page, float y) {
return -y + page.getCropBox().getUpperRightY();
}
Example:
private PDPage drawPage1(PDDocument document) {
PDPage page = new PDPage(PDRectangle.LETTER);
try {
// Gray Color Box
PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, false, false);
contentStream.setNonStrokingColor(Color.decode(MyColors.Gallery));
float [] p1 = getAdjustedPoints(page, 0f, 0f, 8.5f, 1f);
contentStream.addRect(p1[0], p1[1], p1[2], p1[3]);
contentStream.fill();
// Disco Color Box
contentStream.setNonStrokingColor(Color.decode(MyColors.Disco));
p1 = getAdjustedPoints(page, 4.5f, 1f, 4, 0.25f);
contentStream.addRect(p1[0], p1[1], p1[2], p1[3]);
contentStream.fill();
contentStream.close();
} catch (Exception e) { }
return page;
}
As you can see, I've drawn 2 rectangle boxes.
To draw this, I used the the following coordinates which assumes that x=0 and y=0 is top left.
Gray Color Box: x=0, y=0, w=8.5, h=1
Disco Color Box: x=4.5 y=1, w=4, h=0.25
Here's an image of my result.
Add the height of the PDF (Easiest Solution)
I am outputting images to a PDF file using iText. The images always appear larger than they are supposed to. According to the book (iText in Action), this is because iText always displays the images at a resolution of 72 dpi, regardless of what the actual dpi property of the image is. The book suggests using image.getDpiX() to find the dpi of the image, and then using image.scalePercent(72 / actualDpi * 100) to display the image properly. So far, the getDpiX() property of all my images have returned 0 (I've tried 2 gifs and 1 jpg). Is there another way to figure out the actual DPI so that my images scale properly?
com.lowagie.text.Image graphic = com.lowagie.text.Image.getInstance(imgPath);
float actualDpi = graphic.getDpiX();
if (actualDpi > 0)
//Never gets here
graphic.scalePercent(72f / actualDpi * 100);
According to the com.lowagie.text.Image JavaDoc, the method getDpiX gets the dots-per-inch in the X direction. Returns zero if not available.
You're going to have to assume a value when the getDpiX method returns zero. 100 dpi is as good an assumption as any.
if (actualDpi <= 0) actualDpi = 100f;
graphic.scalePercent(72f / actualDpi * 100f);
For GIF, there is no place to store a "DPI" information in the file, so "actualDpi" has no meaning in that case. For JPEG, the "DPI" information can be stored in the file, but is not mandatory, and if not set: "actualDPI" has no meaning as well.
The real answer is: there is no such thing as "actual DPI", either the information is provided (ie. "in this image I want 1 pixel to be rendered with this specific physical width (or height)"), or it's not. Another element is in your sentence: "always appear larger than they are supposed to"; the "supposed to" is the DPI information stored in the image. So if this information is absent, and you feel that when you open the image it seems right on the screen, then you have to calculate the density of your screen (width in number of pixels divided by the width in inches of your screen), and use that as your "actualDPI" variable.
The screen-resolution, retrieved by
java.awt.Toolkit.getDefaultToolkit().getScreenResolution()
did not help, image was still too big. However, one can scale the image to the page width in the following way:
BufferedImage bufferedImage = null; // your image
Image image = Image.getInstance(bufferedImage,null);
image.scalePercent((document.right()-document.left())/((float)image.getWidth())*100);