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.
Related
I am doing some basic experimentation on picture filtering using convolution matrix, based on the Wikipedia page about kernels in image processing.
In order to compute the RGB transformations, I am reading the bitmap via a BufferedImage then get the pixels with getRgb(). While testing the simplest identity filter I noticed that for a specific picture I was getting some grey instead of the original black, while for some other picture, the black was OK.
After more testing, I found that without any transform, a simple BufferedImage -> int[] -> BufferedImage results in the greyed result.
What am I missing ? ImageMagick identify shows that both are 8-bit 256 colors pictures without alpha channels.
betty1.png PNG 339x600 339x600+0+0 8-bit Gray 256c 24526B 0.000u 0:00.000
betty2.jpg JPEG 603x797 603x797+0+0 8-bit Gray 256c 126773B 0.000u 0:00.001
With this picture the result is as expected.
With this one, the result is unexpectedly greyed.
Here is a simple sscce test class to show the problem:
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;
/* simple test class for convolution matrix */
public class CopyPic {
public static void main(String args[]) throws FileNotFoundException, IOException {
if (args.length < 1) {
System.err.println("Usage: CopyPic <picture_file>");
System.exit(1);
}
String imgPath = args[0];
String inputName = imgPath.substring(0, imgPath.lastIndexOf("."));
File ifile = new File(imgPath);
InputStream fis_in = new FileInputStream(ifile);
BufferedImage bi_in = ImageIO.read(fis_in);
fis_in.close();
int width = bi_in.getWidth();
int height = bi_in.getHeight();
System.out.println(String.format("%s = %d x %d", imgPath, width, height));
int[] rgb_in = new int[width * height];
bi_in.getRGB(0, 0, width, height, rgb_in, 0, width);
BufferedImage bi_out = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// for (int y = 0; y < height; y++) {
// for (int x = 0; x < width; x++) {
// bi_out.setRGB(x, y, rgb_out[y * width + x]);
// }
// }
bi_out.setRGB(0, 0, width, height, rgb_in, 0, width);
display(bi_in, bi_out);
String outputName = inputName + "-copy.png";
File ofile = new File(outputName);
OutputStream fos_out = new FileOutputStream(ofile);
ImageIO.write(bi_out, "PNG", fos_out);
fos_out.flush();
fos_out.close();
System.out.println("Wrote " + outputName);
}
// use that to have internal viewer
private static JFrame frame;
private static JLabel label1, label2;
private static void display(BufferedImage img1, BufferedImage img2) {
if (frame == null) {
frame = new JFrame();
frame.setTitle(String.format("%dx%d Original / Copy", img1.getWidth(), img1.getHeight()));
frame.setSize(img1.getWidth() + img2.getWidth(), img1.getHeight());
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
label1 = new JLabel();
label1.setIcon(new ImageIcon(img1));
frame.getContentPane().add(label1, BorderLayout.WEST);
label2 = new JLabel();
label2.setIcon(new ImageIcon(img2));
frame.getContentPane().add(label2, BorderLayout.EAST);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
} else {
label1.setIcon(new ImageIcon(img1));
label2.setIcon(new ImageIcon(img2));
}
}
}
When the ImageIO.read function creates a BufferedImage it uses the type that it thinks is best suited. This type might not be what you expect. In particular, for a JPG image the type might not be TYPE_INT_ARGB.
This is the case for your second image and becomes evident when you print the type of that image:
System.out.println(bi_in.getType());
For that image, this prints 10 on my machine, which represents TYPE_BYTE_GRAY.
So, to fix your problem you should use:
BufferedImage bi_out = new BufferedImage(width, height, bi_in.getType());
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;
}
}
The goal of this code is to have a thumbnail image on the page, and when hovered over by the mouse it shows the image in full size. The code here works with the major caveat that only the final thumbnail-full image combo created will work unless in Adobe Acrobat Pro, I go to Tools > Interactive Objects > Add Button. then all of the buttons visible on the screen will work. If I pass the mouse over any thumbnail, then the large button associated with it is removed from the opened document. I have tried setNeedAppearances(true) and the actions will work for all combos, but the widget formatting is removed. Attached below is my test code. Any help would be greatly appreciated. Thanks.
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import java.io.IOException;
public class TestMain {
public static void main(String[] args) throws IOException {
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
float[] location = new float[]{100 , 600 , 60 , 80};
TestButton.makeButton(document , page , location , "1.jpg");
float[] location2 = new float[]{100 , 400 , 60 , 80};
TestButton.makeButton(document , page , location2 , "2.jpg");
document.save("mytest.pdf");
document.close();
}
}
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionHide;
import org.apache.pdfbox.pdmodel.interactive.action.PDAnnotationAdditionalActions;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDPushButton;
import java.io.IOException;
import java.util.ArrayList;
public class TestButton {
public static void makeButton(PDDocument document , PDPage page , float[] thumbLocation, String imageName) throws IOException {
PDImageXObject image = PDImageXObject.createFromFile("src//main/resources/" + imageName, document);
// thumbLocation array
float thumbX = thumbLocation[0];
float thumbY = thumbLocation[1];
float thumbWidth = thumbLocation[2];
float thumbHeight = thumbLocation[3];
float lowX = page.getMediaBox().getLowerLeftX();
float lowY = page.getMediaBox().getLowerLeftY();
float highY = page.getMediaBox().getUpperRightY();
float fullX = thumbX - image.getWidth() - 10;
if (fullX < lowX + 10){
fullX = thumbX + thumbWidth + 10;
}
float fullY = (thumbY + thumbHeight / 2) - (image.getHeight() / 2);
if (fullY < lowY + 10){
fullY = thumbY + thumbHeight + 10;
}
if (fullY + image.getHeight() > highY - 10){
fullY = thumbY - image.getHeight() - 10;
}
float fullWidth = image.getWidth();
float fullHeight = image.getHeight();
String thumbImage = imageName + "_Thumb";
String fullImage = imageName + "_Full";
PDColor colourBlack = new PDColor(new float[] { 0, 0, 0 }, PDDeviceRGB.INSTANCE);
// Set up thumbnail
COSDictionary thumbDict = new COSDictionary();
PDAcroForm thumbAcro = new PDAcroForm(document , thumbDict);
document.getDocumentCatalog().setAcroForm(thumbAcro);
thumbAcro.setFields(new ArrayList<>());
PDPushButton thumbButton = new PDPushButton(thumbAcro);
thumbButton.setPartialName(thumbImage);
// Thumbnail actions
thumbDict.setItem(COSName.T, new COSString(fullImage));
PDActionHide actionHide = new PDActionHide();
actionHide.setT(thumbDict);
PDActionHide actionShow = new PDActionHide();
actionShow.setT(thumbDict);
actionShow.setH(false);
PDAnnotationAdditionalActions additionalActions = new PDAnnotationAdditionalActions();
additionalActions.setE(actionShow);
additionalActions.setX(actionHide);
// Thumbnail widget
PDAnnotationWidget thumbWidget = thumbButton.getWidgets().get(0);
thumbWidget.setActions(additionalActions);
thumbWidget.setRectangle(new PDRectangle(thumbX, thumbY, thumbWidth, thumbHeight));
// Thumbnail appearance
PDAppearanceDictionary thumbAppearanceDict = new PDAppearanceDictionary();
PDAppearanceStream thumbAppearanceStream = new PDAppearanceStream(document);
thumbAppearanceStream.setResources(new PDResources());
PDAppearanceCharacteristicsDictionary thumbFieldAppearance
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
thumbFieldAppearance.setBorderColour(colourBlack);
thumbWidget.setAppearanceCharacteristics(thumbFieldAppearance);
try (PDPageContentStream thumbContent = new PDPageContentStream(document, thumbAppearanceStream))
{
PDRectangle thumbBox = new PDRectangle(
thumbWidget.getRectangle().getWidth(), thumbWidget.getRectangle().getHeight());
thumbAppearanceStream.setBBox(thumbBox);
thumbContent.setNonStrokingColor(0, 0, 0);
thumbContent.setLineWidth(0.5f);
thumbContent.addRect(thumbBox.getLowerLeftX() + 0.5f, thumbBox.getLowerLeftY() + 0.5f,
thumbBox.getWidth() - 1, thumbBox.getHeight() - 1);
thumbContent.stroke();
thumbContent.drawImage(image , thumbBox.getLowerLeftX() + 0.5f, thumbBox.getLowerLeftY() + 0.5f,
thumbBox.getWidth() - 1, thumbBox.getHeight() - 1);
}
thumbAppearanceDict.setNormalAppearance(thumbAppearanceStream);
thumbWidget.setAppearance(thumbAppearanceDict);
thumbWidget.setHidden(false);
thumbWidget.setPrinted(true);
page.getAnnotations().add(thumbWidget);
thumbAcro.getFields().add(thumbButton);
// Set up full
COSDictionary fullDict = new COSDictionary();
PDAcroForm fullAcro = new PDAcroForm(document , fullDict);
document.getDocumentCatalog().setAcroForm(fullAcro);
fullAcro.setFields(new ArrayList<>());
PDPushButton fullButton = new PDPushButton(fullAcro);
fullButton.setPartialName(fullImage);
// Full widget
PDAnnotationWidget fullWidget = fullButton.getWidgets().get(0);
fullWidget.setRectangle(new PDRectangle(fullX - 1 , fullY - 1, fullWidth + 2 , fullHeight + 2));
// Full appearance
PDAppearanceDictionary fullAppearanceDict = new PDAppearanceDictionary();
PDAppearanceStream fullAppearanceStream = new PDAppearanceStream(document);
fullAppearanceStream.setResources(new PDResources());
PDAppearanceCharacteristicsDictionary fullFieldAppearance
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
fullFieldAppearance.setBorderColour(colourBlack);
fullWidget.setAppearanceCharacteristics(fullFieldAppearance);
try (PDPageContentStream fullContent = new PDPageContentStream(document, fullAppearanceStream))
{
PDRectangle fullBox = new PDRectangle(
fullWidget.getRectangle().getWidth(), fullWidget.getRectangle().getHeight());
fullAppearanceStream.setBBox(fullBox);
fullContent.setNonStrokingColor(0, 0, 0);
fullContent.setLineWidth(1);
fullContent.addRect(fullBox.getLowerLeftX() + 1, fullBox.getLowerLeftY() + 1,
fullBox.getWidth() - 2, fullBox.getHeight() - 2);
fullContent.stroke();
fullContent.drawImage(image, fullBox.getLowerLeftX() + 1, fullBox.getLowerLeftY() + 1,
fullBox.getWidth() - 2, fullBox.getHeight() - 2);
}
fullAppearanceDict.setNormalAppearance(fullAppearanceStream);
fullWidget.setAppearance(fullAppearanceDict);
fullWidget.setHidden(true);
fullWidget.setPrinted(false);
page.getAnnotations().add(fullWidget);
fullAcro.getFields().add(fullButton);
}
}
Thanks mkl, you sent me in the right Direction. I ended up making the TestButton class non-static and moved the Acroform definition to the main class. I am making the AcroForm without the Dictionary as that is set in the TestButton class. Here is the code:
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import java.io.IOException;
import java.util.ArrayList;
public class TestMain {
public static void main(String[] args) throws IOException {
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
PDAcroForm buttonAcro = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(buttonAcro);
buttonAcro.setFields(new ArrayList<>());
TestButton button = new TestButton();
float[] location = new float[]{100 , 600 , 60 , 80};
button.makeButton(document , page , location , "1.jpg" , buttonAcro);
float[] location2 = new float[]{100 , 400 , 60 , 80};
button.makeButton(document , page , location2 , "2.jpg", buttonAcro);
document.save("mytest.pdf");
document.close();
}
}
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionHide;
import org.apache.pdfbox.pdmodel.interactive.action.PDAnnotationAdditionalActions;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDPushButton;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class TestButton {
public void makeButton(PDDocument document, PDPage page, float[] thumbLocation, String imageName,
PDAcroForm buttonAcro) throws IOException {
PDImageXObject image = PDImageXObject.createFromFile("src//main/resources/" + imageName, document);
float thumbX = thumbLocation[0];
float thumbY = thumbLocation[1];
float thumbWidth = thumbLocation[2];
float thumbHeight = thumbLocation[3];
float lowX = page.getMediaBox().getLowerLeftX();
float lowY = page.getMediaBox().getLowerLeftY();
float highY = page.getMediaBox().getUpperRightY();
float fullX = thumbX - image.getWidth() - 10;
if (fullX < lowX + 10){
fullX = thumbX + thumbWidth + 10;
}
float fullY = (thumbY + thumbHeight / 2) - (image.getHeight() / 2);
if (fullY < lowY + 10){
fullY = thumbY + thumbHeight + 10;
}
if (fullY + image.getHeight() > highY - 10){
fullY = thumbY - image.getHeight() - 10;
}
float fullWidth = image.getWidth();
float fullHeight = image.getHeight();
String thumbImage = imageName + "_Thumb";
String fullImage = imageName + "_Full";
PDColor colourBlack = new PDColor(new float[] { 0, 0, 0 }, PDDeviceRGB.INSTANCE);
// Set up thumbnail
COSDictionary thumbDict = new COSDictionary();
PDPushButton thumbButton = new PDPushButton(buttonAcro);
thumbButton.setPartialName(thumbImage);
// Thumbnail actions
thumbDict.setItem(COSName.T, new COSString(fullImage));
PDActionHide actionHide = new PDActionHide();
actionHide.setT(thumbDict);
PDActionHide actionShow = new PDActionHide();
actionShow.setT(thumbDict);
actionShow.setH(false);
PDAnnotationAdditionalActions additionalActions = new PDAnnotationAdditionalActions();
additionalActions.setE(actionShow);
additionalActions.setX(actionHide);
// Thumbnail widget
PDAnnotationWidget thumbWidget = thumbButton.getWidgets().get(0);
thumbWidget.setActions(additionalActions);
thumbWidget.setRectangle(new PDRectangle(thumbX, thumbY, thumbWidth, thumbHeight));
// Thumbnail appearance
PDAppearanceDictionary thumbAppearanceDict = new PDAppearanceDictionary();
PDAppearanceStream thumbAppearanceStream = new PDAppearanceStream(document);
thumbAppearanceStream.setResources(new PDResources());
PDAppearanceCharacteristicsDictionary thumbFieldAppearance
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
thumbFieldAppearance.setBorderColour(colourBlack);
thumbWidget.setAppearanceCharacteristics(thumbFieldAppearance);
try (PDPageContentStream thumbContent = new PDPageContentStream(document, thumbAppearanceStream))
{
PDRectangle thumbBox = new PDRectangle(
thumbWidget.getRectangle().getWidth(), thumbWidget.getRectangle().getHeight());
thumbAppearanceStream.setBBox(thumbBox);
thumbContent.setNonStrokingColor(0, 0, 0);
thumbContent.setLineWidth(0.5f);
thumbContent.addRect(thumbBox.getLowerLeftX() + 0.5f, thumbBox.getLowerLeftY() + 0.5f,
thumbBox.getWidth() - 1, thumbBox.getHeight() - 1);
thumbContent.stroke();
thumbContent.drawImage(image , thumbBox.getLowerLeftX() + 0.5f, thumbBox.getLowerLeftY() + 0.5f,
thumbBox.getWidth() - 1, thumbBox.getHeight() - 1);
}
thumbAppearanceDict.setNormalAppearance(thumbAppearanceStream);
thumbWidget.setAppearance(thumbAppearanceDict);
thumbWidget.setHidden(false);
thumbWidget.setPrinted(true);
page.getAnnotations().add(thumbWidget);
buttonAcro.getFields().add(thumbButton);
// Set up full
PDPushButton fullButton = new PDPushButton(buttonAcro);
fullButton.setPartialName(fullImage);
// Full widget
PDAnnotationWidget fullWidget = fullButton.getWidgets().get(0);
fullWidget.setRectangle(new PDRectangle(fullX - 1 , fullY - 1, fullWidth + 2 , fullHeight + 2));
// Full appearance
PDAppearanceDictionary fullAppearanceDict = new PDAppearanceDictionary();
PDAppearanceStream fullAppearanceStream = new PDAppearanceStream(document);
fullAppearanceStream.setResources(new PDResources());
PDAppearanceCharacteristicsDictionary fullFieldAppearance
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
fullFieldAppearance.setBorderColour(colourBlack);
fullWidget.setAppearanceCharacteristics(fullFieldAppearance);
try (PDPageContentStream fullContent = new PDPageContentStream(document, fullAppearanceStream))
{
PDRectangle fullBox = new PDRectangle(
fullWidget.getRectangle().getWidth(), fullWidget.getRectangle().getHeight());
fullAppearanceStream.setBBox(fullBox);
fullContent.setNonStrokingColor(0, 0, 0);
fullContent.setLineWidth(1);
fullContent.addRect(fullBox.getLowerLeftX() + 1, fullBox.getLowerLeftY() + 1,
fullBox.getWidth() - 2, fullBox.getHeight() - 2);
fullContent.stroke();
fullContent.drawImage(image, fullBox.getLowerLeftX() + 1, fullBox.getLowerLeftY() + 1,
fullBox.getWidth() - 2, fullBox.getHeight() - 2);
}
fullAppearanceDict.setNormalAppearance(fullAppearanceStream);
fullWidget.setAppearance(fullAppearanceDict);
fullWidget.setHidden(true);
fullWidget.setPrinted(false);
page.getAnnotations().add(fullWidget);
buttonAcro.getFields().add(fullButton);
}
}
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.
I have an application that I want to export high-resolution (or rather, high pixel density?) images for printing - for example, I want images that print at 250 dots per inch (DPI), instead of the default, which I understand to be 72 DPI.
I'm using a BufferedImage with a Graphics2D object to draw the image, then ImageIO.write() to save the image.
Any idea how I can set the DPI?
Kurt's answer showed the way, still it took me quite some time to get it run, so here is the code that sets DPI when saving a PNG. There is a lot to do to get the proper writers and such...
private BufferedImage gridImage;
...
private void saveGridImage(File output) throws IOException {
output.delete();
final String formatName = "png";
for (Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName(formatName); iw.hasNext();) {
ImageWriter writer = iw.next();
ImageWriteParam writeParam = writer.getDefaultWriteParam();
ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
continue;
}
setDPI(metadata);
final ImageOutputStream stream = ImageIO.createImageOutputStream(output);
try {
writer.setOutput(stream);
writer.write(metadata, new IIOImage(gridImage, null, metadata), writeParam);
} finally {
stream.close();
}
break;
}
}
private void setDPI(IIOMetadata metadata) throws IIOInvalidTreeException {
// for PMG, it's dots per millimeter
double dotsPerMilli = 1.0 * DPI / 10 / INCH_2_CM;
IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
horiz.setAttribute("value", Double.toString(dotsPerMilli));
IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
vert.setAttribute("value", Double.toString(dotsPerMilli));
IIOMetadataNode dim = new IIOMetadataNode("Dimension");
dim.appendChild(horiz);
dim.appendChild(vert);
IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
root.appendChild(dim);
metadata.mergeTree("javax_imageio_1.0", root);
}
Seting up TIFF DPI
If you want to set dpi for TIFF, try to do that by next steps:
private static IIOMetadata createMetadata(ImageWriter writer, ImageWriteParam writerParams, int resolution) throws
IIOInvalidTreeException
{
// Get default metadata from writer
ImageTypeSpecifier type = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
IIOMetadata meta = writer.getDefaultImageMetadata(type, writerParams);
// Convert default metadata to TIFF metadata
TIFFDirectory dir = TIFFDirectory.createFromMetadata(meta);
// Get {X,Y} resolution tags
BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
TIFFTag tagXRes = base.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION);
TIFFTag tagYRes = base.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
// Create {X,Y} resolution fields
TIFFField fieldXRes = new TIFFField(tagXRes, TIFFTag.TIFF_RATIONAL, 1, new long[][] { { resolution, 1 } });
TIFFField fieldYRes = new TIFFField(tagYRes, TIFFTag.TIFF_RATIONAL, 1, new long[][] { { resolution, 1 } });
// Add {X,Y} resolution fields to TIFFDirectory
dir.addTIFFField(fieldXRes);
dir.addTIFFField(fieldYRes);
// Add unit field to TIFFDirectory (change to RESOLUTION_UNIT_CENTIMETER if necessary)
dir.addTIFFField(new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT), BaselineTIFFTagSet.RESOLUTION_UNIT_INCH));
// Return TIFF metadata so it can be picked up by the IIOImage
return dir.getAsMetadata();
}
Also, similar way you can setting up any TIFF tag.
Read more at the source
i am using this code for tiff file in my project and it works well..
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.media.jai.NullOpImage;
import javax.media.jai.OpImage;
import javax.media.jai.PlanarImage;
import com.sun.media.jai.codec.FileSeekableStream;
import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageDecoder;
import com.sun.media.jai.codec.ImageEncoder;
import com.sun.media.jai.codec.SeekableStream;
import com.sun.media.jai.codec.TIFFEncodeParam;
import com.sun.media.jai.codec.TIFFField;
class SetDDPI
{
static void tiff_Maker(List<BufferedImage> output, String result) throws IOException
{
TIFFEncodeParam params = new TIFFEncodeParam();
OutputStream out = new FileOutputStream(result);
List<BufferedImage> imageList = new ArrayList<BufferedImage>();
for (int i = 1; i < output.size(); i++)
{
imageList.add(output.get(i));
}
params.setWriteTiled(true);
params.setCompression(TIFFEncodeParam.COMPRESSION_GROUP4);
params.setExtraImages(imageList.iterator());
TIFFField[] extras = new TIFFField[2];
extras[0] = new TIFFField(282, TIFFField.TIFF_RATIONAL, 1, (Object) new long[][] { { (long) 300, (long) 1 },
{ (long) 0, (long) 0 } });
extras[1] = new TIFFField(283, TIFFField.TIFF_RATIONAL, 1, (Object) new long[][] { { (long) 300, (long) 1 },
{ (long) 0, (long) 0 } });
params.setExtraFields(extras);
ImageEncoder encoder = ImageCodec.createImageEncoder("tiff", out, params);
encoder.encode(output.get(0));
out.close();
}
static List<BufferedImage> tiff_Extractor(File tiff) throws IOException
{
List<BufferedImage> images = new ArrayList<BufferedImage>();
SeekableStream ss = new FileSeekableStream(tiff);
ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", ss, null);
int numPages = decoder.getNumPages();
for (int j = 0; j < numPages; j++)
{
PlanarImage op = new NullOpImage(decoder.decodeAsRenderedImage(j), null, null, OpImage.OP_IO_BOUND);
images.add(op.getAsBufferedImage());
}
return images;
}
}
this is to set 300 DPI of Tiff image. you can change it according to your need.
extras[0] = new TIFFField(282, TIFFField.TIFF_RATIONAL, 1, (Object) new
long[][] { { (long) 300, (long) 1 },{ (long) 0, (long) 0 } });
extras[1] = new TIFFField(283, TIFFField.TIFF_RATIONAL, 1, (Object) new
long[][] { { (long) 300, (long) 1 },{ (long) 0, (long) 0 } });