iText7 setRotation() not working properly - java

I'm making a small Java program that can modify an existing PDF, and save the changes in a new PDF, using iTextPDF 7. I started with a rotating feature, using setRotation() on a PdfDocument, but my PDF output is rotating 90 degree less than the value I put as a parameter.
So setRotation(90) makes no changes,
setRotation(180) does a single clockwise change,
setRotation(270) does a double turn (180 degree rotation).
etc..
Here's my Code:
import java.io.IOException;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
public class rotateMyPDF {
public static void main(String[] args) throws IOException {
PdfReader in_pdf = new PdfReader("in.pdf");
PdfDocument pdfDoc = new PdfDocument(in_pdf, new PdfWriter("out.pdf"));
for (int i=1; i<=pdfDoc.getNumberOfPages(); i++) {
pdfDoc.getPage(i).setRotation(270); //error here?
}
pdfDoc.close();
System.out.println("done.");
}
}
Seems like a bug? Or is there an error in my code? Or is this intended functionality?
If I put setRotation(0), the PDF pages are rotated 90 degrees counter clockwise!

You appear to misunderstand the setRotation method. It does not add to the current rotation but it sets the rotation value.
Thus, if your page already had page rotation applied, setting the same value with setRotation will change nothing etc.
Your source page appears to already be rotated by 90°. That would explain the observation:
So setRotation(90) makes no changes,
setRotation(180) does a single clockwise change,
setRotation(270) does a double turn (180 degree rotation).
etc..
Simply first read the current rotation value with getRotation, add the desired extra rotation, and then set that sum using setRotation.

Related

PDFClown: Creating a TextMarkup leads to an inaccurate Box of the TextMarkup

Im working with PDFClown to analyze and work with PDFDocuments. My aim is to highlight all numbers within a table. For all numbers which belong together (For example: All numbers in one column of a table) I will create one TextMarkup with a List of Quads. First of all it looks like everythink work well: All highlights on the left belong to one TextMarkup and all Highlights on the right belong to another TextMarkup.
But when analyzing the size of the TextMarkup the size is bigger than it looks at the picture. So when drawing for example a rectangle arround the left TextMarkup box the rectangle intersects the other column despite no highlight of the left TextMarkup intersects the other column. Is there a way to optimize the Box of the TextMarkup? I think there is a bulbous ending of the box so that the box is intersecting the other TextMarkup
This is the code which creates the TextMarkup:
List<Quad> highlightQuads = new ArrayList<Quad>();
for (TextMarkup textMarkup : textMarkupsForOneAnnotation) {
Rectangle2D textBox = textMarkup.getBox();
Rectangle2D.Double rectangle = new Rectangle2D.Double(textBox.getX(), textBox.getY(), textBox.getWidth(), textBox.getHeight());
highlightQuads.add(Quad.get(rectangle));
}
if (highlightQuads.size() > 0) {
TextMarkup _textMarkup = new TextMarkup(pagesOfNewFile.get(lastFoundNewFilePage).getPage(), highlightQuads,"", MarkupTypeEnum.Highlight);
_textMarkup.setColor(DeviceRGBColor.get(Color.GREEN));
_textMarkup.setVisible(true);
allTextMarkUps.add(_textMarkup);
}
Here is an example file Example
Thank You !!
Your code is not really self contained (I cannot run it as it in particular misses the input data), so I could only do a bit of PDF Clown code analysis. That code analysis, though, did indeed turn up a PDF Clown implementation detail that would explain your observation.
How does PDF Clown calculate the dimensions of the markup annotation?
The markup annotation rectangle must be big enough to include all quads plus start and end decorations (rounded left and right caps on markup rectangle).
PDF Clown calculates this rectangle as follows in TextMarkup:
public void setMarkupBoxes(
List<Quad> value
)
{
PdfArray quadPointsObject = new PdfArray();
double pageHeight = getPage().getBox().getHeight();
Rectangle2D box = null;
for(Quad markupBox : value)
{
/*
NOTE: Despite the spec prescription, Point 3 and Point 4 MUST be inverted.
*/
Point2D[] markupBoxPoints = markupBox.getPoints();
quadPointsObject.add(PdfReal.get(markupBoxPoints[0].getX())); // x1.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[0].getY())); // y1.
quadPointsObject.add(PdfReal.get(markupBoxPoints[1].getX())); // x2.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[1].getY())); // y2.
quadPointsObject.add(PdfReal.get(markupBoxPoints[3].getX())); // x4.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[3].getY())); // y4.
quadPointsObject.add(PdfReal.get(markupBoxPoints[2].getX())); // x3.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[2].getY())); // y3.
if(box == null)
{box = markupBox.getBounds2D();}
else
{box.add(markupBox.getBounds2D());}
}
getBaseDataObject().put(PdfName.QuadPoints, quadPointsObject);
/*
NOTE: Box width is expanded to make room for end decorations (e.g. rounded highlight caps).
*/
double markupBoxMargin = getMarkupBoxMargin(box.getHeight());
box.setRect(box.getX() - markupBoxMargin, box.getY(), box.getWidth() + markupBoxMargin * 2, box.getHeight());
setBox(box);
refreshAppearance();
}
private static double getMarkupBoxMargin(
double boxHeight
)
{return boxHeight * .25;}
So it takes the bounding box of all the quads and adds left and right margins each as wide as a quarter of the height of this whole bounding box.
What is the result in your case?
While this added margin width is sensible if there is only a single quad, in case of your markup annotation which includes many quads on top of one another, this results in a giant, unnecessary margin.
How to improve the code?
As the added caps depend on the individual caps and not their combined bounding box, one can improve the code by using the maximum height of the individual quads instead of the height of the bounding box of all quads, e.g. like this:
Rectangle2D box = null;
double maxQuadHeight = 0;
for(Quad markupBox : value)
{
double quadHeight = markupBox.getBounds2D().getHeight();
if (quadHeight > maxQuadHeight)
maxQuadHeight = quadHeight;
...
}
...
double markupBoxMargin = getMarkupBoxMargin(maxQuadHeight);
box.setRect(box.getX() - markupBoxMargin, box.getY(), box.getWidth() + markupBoxMargin * 2, box.getHeight());
setBox(box);
If you don't want to patch PDF Clown for this, you can also execute this code (with minor adaptations) after constructing the TextMarkup _textMarkup to correct the precalculated annotation rectangle.
Is this fixing a PDF Clown error?
It is not an error as there is no need for the text markup annotation rectangle to be minimal; PDF Clown could also always use the whole crop box for each such annotation.
I would assume, though, that the author of the code wanted to calculate a somewhat minimal rectangle but only optimized for single line and so in a way did not live up to his own expectations...
Are there other problems in this code?
Yes. The text a markup annotation marks needs not be horizontal, it may be there at an angle, it could even be vertical. In such a case some margin would also be needed at the top and the bottom of the annotation rectangle, not (only) at the left and the right.

Rotating BufferedImage changes its colors

I'm trying to code a class to seam carve images in x and y direction. The x direction is working, and to reduce the y direction I thought about simply rotating the image 90° and run the same code over the already rescaled image (in x direction only) and after that, rotate it back to its initial state.
I found something with AffineTransform and tried it. It actually produced a rotated image, but messed up the colors and I don't know why.
This is all the code:
import java.awt.image.BufferedImage;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.io.File;
import java.io.IOException;
import javafx.scene.paint.Color;
import javax.imageio.ImageIO;
public class example {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException {
// TODO code application logic here
BufferedImage imgIn = ImageIO.read(new File("landscape.jpg"));
BufferedImage imgIn2 = imgIn;
AffineTransform tx = new AffineTransform();
tx.rotate(Math.PI/2, imgIn2.getWidth() / 2, imgIn2.getHeight() / 2);//(radian,arbit_X,arbit_Y)
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
BufferedImage last = op.filter(imgIn2, null);//(sourse,destination)
ImageIO.write(last, "JPEG", new File("distortedColors.jpg"));
}
}
Just alter the filename in
BufferedImage imgIn = ImageIO.read(new File("landscape.jpg")); and try it.
When executed, you get 4 images: a heatmap, an image with seams in it and a rescaled image. The last image is a test to see if the rotation worked and it should show a rotated image but with distorted colors...
Help would be greatly appreciated!
EDIT:
The problem is with the AffineTransformOp You need :
AffineTransformOp.TYPE_NEAREST_NEIGHBOR
instead of the BILINEAR you have now.
Second paragraph from documentation hints towards this.
This class uses an affine transform to perform a linear mapping from
2D coordinates in the source image or Raster to 2D coordinates in the
destination image or Raster. The type of interpolation that is used is
specified through a constructor, either by a RenderingHints object or
by one of the integer interpolation types defined in this class. If a
RenderingHints object is specified in the constructor, the
interpolation hint and the rendering quality hint are used to set the
interpolation type for this operation.
The color rendering hint and
the dithering hint can be used when color conversion is required. Note
that the following constraints have to be met: The source and
destination must be different. For Raster objects, the number of bands
in the source must be equal to the number of bands in the destination.
So this works
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
It seems like there's a color conversion happening due to passing null to op.filter(imgIn2, null);.
If you change it like that it should work:
BufferedImage last = new BufferedImage( imgIn2.getWidth(), imgIn2.getHeight(), imgIn2.getType() );
op.filter(imgIn2, last );
Building on what bhavya said...
Keep it simple and you should use the dimensions expected from the operation:
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
BufferedImage destinationImage = op.filter(bImage, op.createCompatibleDestImage(bImage, null));

How to move an entire GPen object?

In Java using the acm.graphics GPen is there any way to move the entire drawn sequence of lines? I've read the manual thoroughly and I'm beginning to think it's not possible which brings me to my second question. Are there any other graphics objects in Java that work very similar to a pen that can also be moved. The reason I'm asking is because I've been working on a graphing program that allows mouse gestures to be used to pan around and zoom in and out. After building functionality for implicit functions I realized simply clearing the drawing board and redrawing everything is not going to cut it anymore so I really need to work on more efficient ways to handle intermediate changes of the graph without having to recalculate everything. For example with this or similar code:
GPen p = new GPen();
p.setLocation(100,100); //places the pen on the canvas at 100, 100
p.drawLine(-50,0); //draw a line left 50 pixels
p.drawLine(50,-50); //draw a line right and up 50 pixels each
p.drawLine(0,50); //draw a line down 50 pixels
This would result in a simple right triangle who's bottom right most point is at 100, 100 on a particular canvas. What I need to do is be able to move this same drawn sequence of lines relative to one another to another origin. What I hoping for is a class that has separate methods for setLocation() and move() where setLocation() controls pen position and move() would move the entire object around.
Ok so having received almost no attention on here I've came to the conclusion that such a method just needs to be written from scratch and went ahead and did that. I'm not entirely sure how helpful posting my proprietary code would be but in the event that anybody could use it I'll post the basic idea of it. Since Pen utilities are essentially a bunch of lines and lines are a bunch of from and to's I created an object that I called FPen (for FunctionPen) that accepts the instructions for from and to. While defining FPen you pass it where to start and how far to go however many times you need and that's it. Once you've passed these instructions I created another method called returnGPen(Color c) which will on call use the instructions it has on hand and generate the desired GPen object. When you want to move the entire GPen you can then create a method called adjustOrigin(double oX, double oY); which will calculate a change from a previously recorded origin and this new one and go through the list of instructions and adjust them appropriately.
My needs for this Class are strictly for my Graphing program and are not entirely finished either but it does work for most purposes.
import acm.graphics.GPen;
import java.awt.Color;
import java.util.ArrayList;
public class FPen{
private double relativeCenterX;
private double relativeCenterY;
private ArrayList<Double> fromX = new ArrayList<Double>();
private ArrayList<Double> fromY = new ArrayList<Double>();
private ArrayList<Double> distX = new ArrayList<Double>();
private ArrayList<Double> distY = new ArrayList<Double>();
public FPen(double rX, double rY, double z){
relativeCenterX = rX;
relativeCenterY = rY;
}
public void adjustOrigin(double cX, double cY){
double changeX = relativeCenterX-cX;
double changeY = relativeCenterY-cY;
for(int i = 0; i < fromX.size(); i++){
fromX.set(i,fromX.get(i)+changeX*zoom);
fromY.set(i,fromY.get(i)-changeY*zoom);
}
relativeCenterX = cX;
relativeCenterY = cY;
}
public void clear(){
fromX.clear();
fromY.clear();
distX.clear();
distY.clear();
}
public void drawLine(double fX, double fY, double tX, double tY){
fromX.add(fX);
fromY.add(fY);
distX.add(tX);
distY.add(tY);
}
public GPen returnGPen(Color c){
GPen pen = new GPen();
pen.setColor(c);
for(int i = 0; i < fromX.size(); i++){
pen.setLocation(fromX.get(i),fromY.get(i));
pen.drawLine(distX.get(i),distY.get(i));
}
return pen;
}
}
Of course a unexpected nice thing that came out of this was the idea that I can now quickly benchmark different drawing routines by creating different methods for each and calling what I'm interested in.

Recognizing numbers in an image in java

I want to recognize numbers in the following image
I am currently using Tess4J library in eclipse java project but it only recognizes the characters in a plane color background. For this image it could not even identify that there are characters(numbers) on this image. Help me find a way to accomplish this task.
Here is my current code:
import net.sourceforge.tess4j.*;
import java.io.File;
public class Main {
public static void main(String[] args) {
File imageFile = new File("image.png");
Tesseract instance = Tesseract.getInstance();
try {
String result = instance.doOCR(imageFile);
System.out.println(result);
} catch (TesseractException e) {
System.err.println(e.getMessage());
}
}
}
and if there is way to count the squares separated by yellow lines.
Thank you
If your image is representative, then all you need as a first step is a binarization at a threshold close to the maximum value followed by discarding of small components.
f = Import["http://i.stack.imgur.com/6AXwH.jpg"]
step1 = SelectComponents[Binarize[ColorConvert[f, "Grayscale"], 0.9],
"Count", #1 > 100 &]
Now, if you know that the digits cannot be too tall or too thin (this is dependent on image dimensions), then you can filter the remaining components based on its bounding box.
SelectComponents[step1, "BoundingBox",
And[10 < #[[2, 1]] - #[[1, 1]] < 100, 50 < #[[2, 2]] - #[[1, 2]] < 100] &]
To separate each of the regions, you could consider using a colorspace where there is a channel dedicated to the yellow color. CMYK is a possibility here, and again all you need is a threshold at a high value, together with the basic morphological closing to complete the lines (since in your example the lines do not extend to the border of the image). Instead of using morphological closings here, you could detect the lines using Hough or RANSAC, for example.
rects = Closing[
Closing[Binarize[ColorSeparate[f, "CMYK"][[3]], 0.9],
ConstantArray[1, {1, 15}]], ConstantArray[1, {15, 1}]] (* left image *)
Colorize[MorphologicalComponents[ColorNegate[rects]],
ColorFunction -> "Rainbow"] (* right image *)
The tools used here are very simple, and almost any image processing library will provide them. There are also more robust approaches that could be taken, but for the given image it is not needed.

Mouse shows color

i am trying to make an application which would show which color my mouse is pointing to, i dont mean in my own application but anywhere in windows on any screen, kind of like a tag beside my mouse pointer which shows the exact color.
I am a Java developer but i dont think this could be done in java i am thinking maybe i need some sort of script but i have no idea any help would be really appriciated
The solution consists of two parts:
Part 1: Retrieving the color:
Point mouseLocation = MouseInfo.getPointerInfo().getLocation();
Color color = new Robot().getPixelColor(mouseLocation.x, mouseLocation.y);
Part 2: Getting the color name:
You can get a list of many colors and their names from Wikipedia's List of colors. You can create a mapping in Java given the data on Wikipedia.
Perhaps you can start with a few colors, and provide a generic hex representation for unknown colors, for example #rrggbb.
Here is the runnable example,
import java.awt.AWTException;
import java.awt.Color;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
public class Main {
public static String getHexString(int rgb) {
String hexString = Integer.toHexString(rgb);
hexString = hexString.length() > 1 ? hexString : "0" + hexString;
return hexString;
}
public static void main(String[] a) throws AWTException {
Point mouseLocation = MouseInfo.getPointerInfo().getLocation();
Color color = new Robot().getPixelColor(mouseLocation.x,
mouseLocation.y);
System.out.println(getHexString(color.getRed())
+ getHexString(color.getGreen())
+ getHexString(color.getBlue()));
}
}
Take your pick: http://rosettacode.org/wiki/Color_of_a_screen_pixel
There is a Java/AWT example, an AutoHotKey is a simple scripted option.
The second C example shows the 3 API calls you need GetDC/GetCursorPos/GetPixel and their support code, these can be used from most languages that compile for windows.

Categories