I am trying to extract all text in a pdf along with their coordinates.
I am using Apache PDFBox 2.0.8 and following the sample program DrawPrintTextLocations .
It seems to work mostly, but for certain pdf-s i get negative values for the x and y coordinates of the bounding boxes. Refer this pdf file for example.
My app assumes the coordinate system as a normal pdf (x goes from left to right an y goes top to bottom). so these are throwing my computations off.
Below is the relevant piece of code.
import org.apache.fontbox.util.BoundingBox;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType3Font;
import org.apache.pdfbox.pdmodel.interactive.pagenavigation.PDThreadBead;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.List;
/**
* This is an example on how to get some x/y coordinates of text and to show them in a rendered
* image.
*
* #author Ben Litchfield
* #author Tilman Hausherr
*/
public class DrawPrintTextLocations extends PDFTextStripper {
private AffineTransform flipAT;
private AffineTransform rotateAT;
private AffineTransform transAT;
private final float DPI = 200.0f;
private final double PT2PX = DPI / 72.0;
private final AffineTransform dpiAT = AffineTransform.getScaleInstance(PT2PX, PT2PX);
private final String filename;
static final int SCALE = 1;
private Graphics2D g2d;
private final PDDocument document;
/**
* Instantiate a new PDFTextStripper object.
*
* #param document
* #param filename
* #throws IOException If there is an error loading the properties.
*/
public DrawPrintTextLocations(PDDocument document, String filename) throws IOException {
this.document = document;
this.filename = filename;
}
/**
* This will print the documents data.
*
* #param args The command line arguments.
* #throws IOException If there is an error parsing the document.
*/
public static void main(String[] args) throws IOException {
String pdfLoc = "/debug/pdfbox/p2_VS008PI.pdf";
if (args.length == 1) {
pdfLoc = args[0];
}
try (PDDocument document = PDDocument.load(new File(pdfLoc))) {
DrawPrintTextLocations stripper = new DrawPrintTextLocations(document, pdfLoc);
stripper.setSortByPosition(true);
for (int page = 0; page < document.getNumberOfPages(); ++page) {
stripper.stripPage(page);
}
}
}
private void stripPage(int page) throws IOException {
PDFRenderer pdfRenderer = new PDFRenderer(document);
BufferedImage image = pdfRenderer.renderImageWithDPI(page, DPI);
PDPage pdPage = document.getPage(page);
PDRectangle cropBox = pdPage.getCropBox();
// flip y-axis
flipAT = new AffineTransform();
flipAT.translate(0, pdPage.getBBox().getHeight());
flipAT.scale(1, -1);
// page may be rotated
rotateAT = new AffineTransform();
int rotation = pdPage.getRotation();
if (rotation != 0) {
PDRectangle mediaBox = pdPage.getMediaBox();
switch (rotation) {
case 90:
rotateAT.translate(mediaBox.getHeight(), 0);
break;
case 270:
rotateAT.translate(0, mediaBox.getWidth());
break;
case 180:
rotateAT.translate(mediaBox.getWidth(), mediaBox.getHeight());
break;
default:
break;
}
rotateAT.rotate(Math.toRadians(rotation));
}
// cropbox
transAT = AffineTransform.getTranslateInstance(-cropBox.getLowerLeftX(), cropBox.getLowerLeftY());
g2d = image.createGraphics();
g2d.setStroke(new BasicStroke(0.1f));
g2d.scale(SCALE, SCALE);
setStartPage(page + 1);
setEndPage(page + 1);
Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream());
writeText(document, dummy);
g2d.dispose();
String imageFilename = filename;
int pt = imageFilename.lastIndexOf('.');
imageFilename = imageFilename.substring(0, pt) + "-marked-" + (page + 1) + ".png";
ImageIO.write(image, "png", new File(imageFilename));
}
/**
* Override the default functionality of PDFTextStripper.
*/
#Override
protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
for (TextPosition text : textPositions) {
AffineTransform at = text.getTextMatrix().createAffineTransform();
PDFont font = text.getFont();
BoundingBox bbox = font.getBoundingBox();
float xadvance = font.getWidth(text.getCharacterCodes()[0]); // todo: should iterate all chars
Rectangle2D.Float rect1 = new Rectangle2D.Float(0, bbox.getLowerLeftY(), xadvance, bbox.getHeight());
if (font instanceof PDType3Font) {
at.concatenate(font.getFontMatrix().createAffineTransform());
} else {
at.scale(1 / 1000f, 1 / 1000f);
}
Shape s1 = at.createTransformedShape(rect1);
s1 = flipAT.createTransformedShape(s1);
s1 = rotateAT.createTransformedShape(s1);
s1 = dpiAT.createTransformedShape(s1);
g2d.setColor(Color.blue);
g2d.draw(s1);
Rectangle bounds = s1.getBounds();
if (bounds.getX() < 0 || bounds.getY() < 0) {
// THIS is where things go wrong
// i need these coordinates to be +ve
System.out.println(bounds.toString());
System.out.println(rect1.toString());
}
}
}
}
And here is some snippet of the output from the first page of the above pdf.
SECTION 10 – INSURANCE & OTHER FINANCIAL RESOURCES
java.awt.Rectangle[x=-3237,y=40,width=19,height=43]
java.awt.Rectangle[x=-3216,y=40,width=20,height=43]
java.awt.Rectangle[x=-3194,y=40,width=23,height=43]
java.awt.Rectangle[x=-3170,y=40,width=22,height=43]
The characters with negative coordinates are outside the cropbox (also characters with coordinates bigger than the cropbox height / width). See the cropbox as a cutout from something bigger. To see the whole thing, run this code
pdPage.setCropBox(pdPage.getMediaBox());
for each page of your PDF and then save and view it.
Per your comment
Following your advice of setting the crop box to the media box, actually changed the whole on screen appearance of the pdf, now i got 3 pages collated as one.
This suggests that physically, this is a folded sheet that has 3 pages on each side. The online PDF displays this as 6 pages for easy viewing on a computer.
Related
Recently I am trying to implement an image object detection tool based on YOLO. To start with, I have used the codes here. Things sounds fine except the fact the program doesnt pass the following line of code (line 72) and will not go into the loop. :
if (cap.read(frame))
In other words, if a break point is placed at that line, the program wont go to next step.. Any idea how to fix this?
package yoloexample;
import org.opencv.core.*;
import org.opencv.dnn.*;
import org.opencv.utils.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Yoloexample {
private static List<String> getOutputNames(Net net) {
List<String> names = new ArrayList<>();
List<Integer> outLayers = net.getUnconnectedOutLayers().toList();
List<String> layersNames = net.getLayerNames();
outLayers.forEach((item) -> names.add(layersNames.get(item - 1)));//unfold and create R-CNN layers from the loaded YOLO model//
System.out.println(names);
return names;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
System.load("\\opencv\\opencv\\build\\java\\x64\\opencv_java420.dll"); // Load the openCV 4.0 dll //
String modelWeights = "g:\\yolov3.weights"; //Download and load only wights for YOLO , this is obtained from official YOLO site//
String modelConfiguration = "g:\\yolov3.cfg";//Download and load cfg file for YOLO , can be obtained from official site//
String filePath = "test.mp4"; //My video file to be analysed//
VideoCapture cap = new VideoCapture(filePath);// Load video using the videocapture method//
Mat frame = new Mat(); // define a matrix to extract and store pixel info from video//
//cap.read(frame);
JFrame jframe = new JFrame("Video"); // the lines below create a frame to display the resultant video with object detection and localization//
JLabel vidpanel = new JLabel();
jframe.setContentPane(vidpanel);
jframe.setSize(600, 600);
jframe.setVisible(true);// we instantiate the frame here//
Net net = Dnn.readNetFromDarknet(modelConfiguration, modelWeights); //OpenCV DNN supports models trained from various frameworks like Caffe and TensorFlow. It also supports various networks architectures based on YOLO//
//Thread.sleep(5000);
//Mat image = Imgcodecs.imread("D:\\yolo-object-detection\\yolo-object-detection\\images\\soccer.jpg");
Size sz = new Size(288, 288);
List<Mat> result = new ArrayList<>();
List<String> outBlobNames = getOutputNames(net);
while (true) {
if (cap.read(frame)) {
Mat blob = Dnn.blobFromImage(frame, 0.00392, sz, new Scalar(0), true, false); // We feed one frame of video into the network at a time, we have to convert the image to a blob. A blob is a pre-processed image that serves as the input.//
net.setInput(blob);
net.forward(result, outBlobNames); //Feed forward the model to get output //
// outBlobNames.forEach(System.out::println);
// result.forEach(System.out::println);
float confThreshold = 0.6f; //Insert thresholding beyond which the model will detect objects//
List<Integer> clsIds = new ArrayList<>();
List<Float> confs = new ArrayList<>();
List<Rect> rects = new ArrayList<>();
for (int i = 0; i < result.size(); ++i) {
// each row is a candidate detection, the 1st 4 numbers are
// [center_x, center_y, width, height], followed by (N-4) class probabilities
Mat level = result.get(i);
for (int j = 0; j < level.rows(); ++j) {
Mat row = level.row(j);
Mat scores = row.colRange(5, level.cols());
Core.MinMaxLocResult mm = Core.minMaxLoc(scores);
float confidence = (float) mm.maxVal;
Point classIdPoint = mm.maxLoc;
if (confidence > confThreshold) {
int centerX = (int) (row.get(0, 0)[0] * frame.cols()); //scaling for drawing the bounding boxes//
int centerY = (int) (row.get(0, 1)[0] * frame.rows());
int width = (int) (row.get(0, 2)[0] * frame.cols());
int height = (int) (row.get(0, 3)[0] * frame.rows());
int left = centerX - width / 2;
int top = centerY - height / 2;
clsIds.add((int) classIdPoint.x);
confs.add((float) confidence);
rects.add(new Rect(left, top, width, height));
}
}
}
float nmsThresh = 0.5f;
MatOfFloat confidences = new MatOfFloat(Converters.vector_float_to_Mat(confs));
Rect[] boxesArray = rects.toArray(new Rect[0]);
MatOfRect boxes = new MatOfRect(boxesArray);
MatOfInt indices = new MatOfInt();
Dnn.NMSBoxes(boxes, confidences, confThreshold, nmsThresh, indices); //We draw the bounding boxes for objects here//
int[] ind = indices.toArray();
int j = 0;
for (int i = 0; i < ind.length; ++i) {
int idx = ind[i];
Rect box = boxesArray[idx];
Imgproc.rectangle(frame, box.tl(), box.br(), new Scalar(0, 0, 255), 2);
//i=j;
System.out.println(idx);
}
// Imgcodecs.imwrite("D://out.png", image);
//System.out.println("Image Loaded");
ImageIcon image = new ImageIcon(Mat2bufferedImage(frame)); //setting the results into a frame and initializing it //
vidpanel.setIcon(image);
vidpanel.repaint();
System.out.println(j);
System.out.println("Done");
}
}
}
private static BufferedImage Mat2bufferedImage(Mat image) { // The class described here takes in matrix and renders the video to the frame //
MatOfByte bytemat = new MatOfByte();
Imgcodecs.imencode(".jpg", image, bytemat);
byte[] bytes = bytemat.toArray();
InputStream in = new ByteArrayInputStream(bytes);
BufferedImage img = null;
try {
img = ImageIO.read(in);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return img;
}
}
I am new to itext7.
I saw an example about columndocumentrenderer. code below.
package com.itextpdf.highlevel.chapter02;
import com.itextpdf.io.font.FontConstants;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.ColumnDocumentRenderer;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.hyphenation.HyphenationConfig;
import com.itextpdf.layout.property.AreaBreakType;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.test.annotations.WrapToTest;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
/**
*
* #author RNDPC-03
*/#WrapToTest
public class C02E08_JekyllHydeV4 {
public static final String SRC = "src/main/resources/txt/jekyll_hyde.txt";
public static final String DEST = "results/chapter02/jekyll_hyde_v4.pdf";
public static void main(String args[]) throws IOException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new C02E08_JekyllHydeV4().createPdf(DEST);
}
public void createPdf(String dest) throws IOException {
//Initialize PDF document
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
// Initialize document
Document document = new Document(pdf);
//Set column parameters
float offSet = 36;
float gutter = 23;
float columnWidth = (PageSize.A4.getWidth() - offSet * 2) / 2 - gutter;
float columnHeight = PageSize.A4.getHeight() - offSet * 2;
//Define column areas
Rectangle[] columns = {
new Rectangle(offSet, offSet, columnWidth, columnHeight),
new Rectangle(offSet + columnWidth + gutter, offSet, columnWidth, columnHeight)};
document.setRenderer(new ColumnDocumentRenderer(document, columns));
PdfFont font = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);
PdfFont bold = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD);
document.setTextAlignment(TextAlignment.JUSTIFIED)
.setFont(font)
.setHyphenation(new HyphenationConfig("en", "uk", 3, 3));
BufferedReader br = new BufferedReader(new FileReader(SRC));
String line;
Paragraph p;
boolean title = true;
AreaBreak nextArea = new AreaBreak(AreaBreakType.NEXT_AREA);
while ((line = br.readLine()) != null) {
p = new Paragraph(line);
if (title) {
p.setFont(bold).setFontSize(12);
title = false;
}
else {
p.setFirstLineIndent(36);
}
if (line.isEmpty()) {
document.add(nextArea);
title = true;
}
document.add(p);
}
//Close document
document.close();
}
}
now this will have a result of this . picture below
now I want to make it into a three(3) column. how to do this? please anyone? or just guide me. thank you!
Here is where you define two columns:
float offSet = 36;
float gutter = 23;
float columnWidth = (PageSize.A4.getWidth() - offSet * 2) / 2 - gutter;
float columnHeight = PageSize.A4.getHeight() - offSet * 2;
//Define column areas
Rectangle[] columns = {
new Rectangle(offSet, offSet, columnWidth, columnHeight),
new Rectangle(offSet + columnWidth + gutter, offSet, columnWidth, columnHeight)};
Changing this to three columns is a no-brainer.
Just replace all of the above by something like this:
Rectangle[] columns = {
new Rectangle(20, 20, 180, 802),
new Rectangle(207, 15, 180, 802),
new Rectangle(394, 15, 180, 802) };
Now that columns has three Rectangle elements, three columns will be drawn. Feel free to adapt the values of x, y, width and height in the Rectangle constructor if the three columns don't have the desired size.
I'm looking into generating a pdf-document. At the moment I'm trying out different approaches. I want to get more than one line in a pdf-document. Using a HelloWorld code example I came up with ...
package org.apache.pdfbox.examples.pdmodel;
import java.io.IOException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
/**
* Creates a "Hello World" PDF using the built-in Helvetica font.
*
* The example is taken from the PDF file format specification.
*/
public final class HelloWorld
{
private HelloWorld()
{
}
public static void main(String[] args) throws IOException
{
String filename = "line.pdf";
String message = "line";
PDDocument doc = new PDDocument();
try
{
PDPage page = new PDPage();
doc.addPage(page);
PDFont font = PDType1Font.HELVETICA_BOLD;
PDPageContentStream contents = new PDPageContentStream(doc, page);
contents.beginText();
contents.setFont(font, 12);
// Loop to create 25 lines of text
for (int y = 0; y< 25; y++) {
int ty = 700 + y * 15;
contents.newLineAtOffset(100, ty);
//contents.newLineAtOffset(125, ty);
//contents.showText(Integer.toString(i));
contents.showText(message + " " + Integer.toString(i));
System.out.println(message + " " + Integer.toString(i));
}
contents.endText();
contents.close();
doc.save(filename);
}
finally
{
doc.close();
System.out.println("HelloWorld finished after 'doc.close()'.");
}
}
}
But looking at my resulting document I only see "line 0" once, and no other lines. What am I doing wrong?
Your issue is that you think PDPageContentStream.newLineAtOffset uses absolute coordinates. This is not the case, it uses relative coordinates, cf. the JavaDocs:
/**
* The Td operator.
* Move to the start of the next line, offset from the start of the current line by (tx, ty).
*
* #param tx The x translation.
* #param ty The y translation.
* #throws IOException If there is an error writing to the stream.
* #throws IllegalStateException If the method was not allowed to be called at this time.
*/
public void newLineAtOffset(float tx, float ty) throws IOException
So your additional lines are way off the visible page area.
Thus, you might want to something like this:
...
contents.beginText();
contents.setFont(font, 12);
contents.newLineAtOffset(100, 700);
// Loop to create 25 lines of text
for (int i = 0; i < 25; i++) {
contents.showText(message + " " + Integer.toString(i));
System.out.println(message + " " + Integer.toString(i));
contents.newLineAtOffset(0, -15);
}
contents.endText();
...
Here you start at 100, 700 and move down for each line by 15.
In addition to mkl's answer you could also create a new text operation for each line. Doing that will enable you to use absolute coordinates.
...
contents.setFont(font, 12);
// Loop to create 25 lines of text
for (int i = 0; i < 25; i++) {
int ty = 700 + y * 15;
contents.beginText();
contents.newLineAtOffset(100, ty);
contents.showText(message + " " + Integer.toString(i));
System.out.println(message + " " + Integer.toString(i))
contents.endText();
}
...
Whether you need this or not depends on your usecase.
For example I wanted to write some text right aligned. In that case it was easier to use absolute position, so I created a helper method like this:
public static void showTextRightAligned(PDPageContentStream contentStream, PDType1Font font, int fontsize, float rightX, float topY, String text) throws IOException
{
float textWidth = fontsize * font.getStringWidth(text) / 1000;
float leftX = rightX - textWidth;
contentStream.beginText();
contentStream.newLineAtOffset(leftX, topY);
contentStream.showText(text);
contentStream.endText();
}
You could do something like this:
contentStream.beginText();
contentStream.newLineAtOffset(20,750);
//This begins the cursor at top right
contentStream.setFont(PDType1Font.TIMES_ROMAN,8);
for (String readList : resultList) {
contentStream.showText(readList);
contentStream.newLineAtOffset(0,-12);
//This will move cursor down by 12pts on every run of loop
}
I'm trying to write strings to images, so it's harder to copy the text and run it through a translator.
My code works fine, but I get always a really long image - I rather would like to have a more readable box in where the string is written. My method "StringDiver" does add "\n" but it does not help when writing the string to an image.
Right now I get this output.
Any hint what I could do?
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class writeToImage {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String newString = "Mein Eindruck ist, dass die politische und öffentliche Meinung in Deutschland anfängt, die wirtschaftliche Zerstörung im Inland und in Europa zu erkennen, die auf einen eventuellen Zusammenbruch des Euro folgen würde.";
String sampleText = StringDivider(newString);
//Image file name
String fileName = "Image";
//create a File Object
File newFile = new File("./" + fileName + ".jpg");
//create the font you wish to use
Font font = new Font("Tahoma", Font.PLAIN, 15);
//create the FontRenderContext object which helps us to measure the text
FontRenderContext frc = new FontRenderContext(null, true, true);
//get the height and width of the text
Rectangle2D bounds = font.getStringBounds(sampleText, frc);
int w = (int) bounds.getWidth();
int h = (int) bounds.getHeight();
//create a BufferedImage object
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
//calling createGraphics() to get the Graphics2D
Graphics2D g = image.createGraphics();
//set color and other parameters
g.setColor(Color.WHITE);
g.fillRect(0, 0, w, h);
g.setColor(Color.BLACK);
g.setFont(font);
g.drawString(sampleText, (float) bounds.getX(), (float) -bounds.getY());
//releasing resources
g.dispose();
//creating the file
try {
ImageIO.write(image, "jpg", newFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static String StringDivider(String s){
StringBuilder sb = new StringBuilder(s);
int i = 0;
while ((i = sb.indexOf(" ", i + 30)) != -1) {
sb.replace(i, i + 1, "\n");
}
return sb.toString();
}
}
g.drawString(sampleText, (float) bounds.getX(), (float) -bounds.getY());
Split text and write every part to image.
Rectangle2D bounds = font.getStringBounds(sampleText, frc);
int w = (int) bounds.getWidth();
int h = (int) bounds.getHeight();
String[] parts = sampleText.split("\n");
//create a BufferedImage object
BufferedImage image = new BufferedImage(w, h * parts.length, BufferedImage.TYPE_INT_RGB);
int index = 0;
for(String part : parts){
g.drawString(part, 0, h * index++);
}
ex:
first part: x=0 ; y=0
second part: x=0 ; y=5
third part: x=0 ; y=10;
heightText = h
Take a look at LineBreakMeasurer. The first code example in the Javadoc is exactly what you're looking for.
I wrote a program that generates a BufferedImage to be displayed on the screen and then printed. Part of the image includes grid lines that are 1 pixel wide. That is, the line is 1 pixel, with about 10 pixels between lines. Because of screen resolution, the image is displayed much bigger than that, with several pixels for each line. I'd like to draw it smaller, but when I scale the image (either by using Image.getScaledInstance or Graphics2D.scale), I lose significant amounts of detail.
I'd like to print the image as well, and am dealing with the same problem. In that case, I am using this code to set the resolution:
HashPrintRequestAttributeSet set = new HashPrintRequestAttributeSet();
PrinterResolution pr = new PrinterResolution(250, 250, ResolutionSyntax.DPI);
set.add(pr);
job.print(set);
which works to make the image smaller without losing detail. But the problem is that the image is cut off at the same boundary as if I hadn't set the resolution. I'm also confused because I expected a larger number of DPI to make a smaller image, but it's working the other way.
I'm using java 1.6 on Windows 7 with eclipse.
Regarding the image being cut-off on the page boundary, have you checked the clip region of the graphics? I mean try :
System.out.println(graphics.getClipBounds());
and make sure it is correctly set.
I had the same problem. Here is my solution.
First change the resolution of the print job...
PrinterJob job = PrinterJob.getPrinterJob();
// Create the paper size of our preference
double cmPx300 = 300.0 / 2.54;
Paper paper = new Paper();
paper.setSize(21.3 * cmPx300, 29.7 * cmPx300);
paper.setImageableArea(0, 0, 21.3 * cmPx300, 29.7 * cmPx300);
PageFormat format = new PageFormat();
format.setPaper(paper);
// Assign a new print renderer and the paper size of our choice !
job.setPrintable(new PrintReport(), format);
if (job.printDialog()) {
try {
HashPrintRequestAttributeSet set = new HashPrintRequestAttributeSet();
PrinterResolution pr = new PrinterResolution((int) (dpi), (int) (dpi), ResolutionSyntax.DPI);
set.add(pr);
job.setJobName("Jobname");
job.print(set);
} catch (PrinterException e) {
}
}
Now you can draw everything you like into the new high resolution paper like this !
public class PrintReport implements Printable {
#Override
public int print(Graphics g, PageFormat pf, int page) throws PrinterException {
// Convert pixels to cm to lay yor page easy on the paper...
double cmPx = dpi / 2.54;
Graphics2D g2 = (Graphics2D) g;
int totalPages = 2; // calculate the total pages you have...
if (page < totalPages) {
// Draw Page Header
try {
BufferedImage image = ImageIO.read(ClassLoader.getSystemResource(imgFolder + "largeImage.png"));
g2.drawImage(image.getScaledInstance((int) (4.8 * cmPx), -1, BufferedImage.SCALE_SMOOTH), (int) (cmPx),
(int) (cmPx), null);
} catch (IOException e) {
}
// Draw your page as you like...
// End of Page
return PAGE_EXISTS;
} else {
return NO_SUCH_PAGE;
}
}
It sounds like your problem is that you are making the grid lines part of the BufferedImage and it doesn't look good when scaled. Why not use drawLine() to produce the grid after your image has been drawn?
Code for Convert image with dimensions using Java and print the converted image.
Class: ConvertImageWithDimensionsAndPrint.java
package com.test.convert;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class ConvertImageWithDimensionsAndPrint {
private static final int IMAGE_WIDTH = 800;
private static final int IMAGE_HEIGHT = 1000;
public static void main(String[] args) {
try {
String sourceDir = "C:/Images/04-Request-Headers_1.png";
File sourceFile = new File(sourceDir);
String destinationDir = "C:/Images/ConvertedImages/";//Converted images save here
File destinationFile = new File(destinationDir);
if (!destinationFile.exists()) {
destinationFile.mkdir();
}
if (sourceFile.exists()) {
String fileName = sourceFile.getName().replace(".png", "");
BufferedImage bufferedImage = ImageIO.read(sourceFile);
int type = bufferedImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : bufferedImage.getType();
BufferedImage resizedImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, type);
Graphics2D graphics2d = resizedImage.createGraphics();
graphics2d.drawImage(bufferedImage, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);//resize goes here
graphics2d.dispose();
ImageIO.write(resizedImage, "png", new File( destinationDir + fileName +".png" ));
int oldImageWidth = bufferedImage.getWidth();
int oldImageHeight = bufferedImage.getHeight();
System.out.println(sourceFile.getName() +" OldFile with Dimensions: "+ oldImageWidth +"x"+ oldImageHeight);
System.out.println(sourceFile.getName() +" ConvertedFile converted with Dimensions: "+ IMAGE_WIDTH +"x"+ IMAGE_HEIGHT);
//Print the image file
PrintActionListener printActionListener = new PrintActionListener(resizedImage);
printActionListener.run();
} else {
System.err.println(destinationFile.getName() +" File not exists");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Reference of PrintActionListener.java
package com.test.convert;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
public class PrintActionListener implements Runnable {
private BufferedImage image;
public PrintActionListener(BufferedImage image) {
this.image = image;
}
#Override
public void run() {
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(new ImagePrintable(printJob, image));
if (printJob.printDialog()) {
try {
printJob.print();
} catch (PrinterException prt) {
prt.printStackTrace();
}
}
}
public class ImagePrintable implements Printable {
private double x, y, width;
private int orientation;
private BufferedImage image;
public ImagePrintable(PrinterJob printJob, BufferedImage image) {
PageFormat pageFormat = printJob.defaultPage();
this.x = pageFormat.getImageableX();
this.y = pageFormat.getImageableY();
this.width = pageFormat.getImageableWidth();
this.orientation = pageFormat.getOrientation();
this.image = image;
}
#Override
public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (pageIndex == 0) {
int pWidth = 0;
int pHeight = 0;
if (orientation == PageFormat.PORTRAIT) {
pWidth = (int) Math.min(width, (double) image.getWidth());
pHeight = pWidth * image.getHeight() / image.getWidth();
} else {
pHeight = (int) Math.min(width, (double) image.getHeight());
pWidth = pHeight * image.getWidth() / image.getHeight();
}
g.drawImage(image, (int) x, (int) y, pWidth, pHeight, null);
return PAGE_EXISTS;
} else {
return NO_SUCH_PAGE;
}
}
}
}
Output:
04-Request-Headers_1.png OldFile with Dimensions: 1224x1584
04-Request-Headers_1.png ConvertedFile converted with Dimensions: 800x1000
After conversion of a image a Print window will be open for printing the converted image. The window displays like below, Select the printer from Name dropdown and Click OK button.
You can use either of the following to improve the quality of the scaling. I believe BiCubic gives better results but is slower than BILINEAR.
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
I would also not use Image.getScaledInstance() as it is very slow. I'm not sure about the printing as I'm struggling with similar issues.