Select with mouse on a PDF (User Input) - java

I'm currently using PDFBox and I'm trying to open a PDF so that the user can select with his mouse areas to crop, I've no idea how to proceed to make a PDF viewer & let the user input the selected rectangle to crop.
PDPage#setCropBox(PDRectangle cropBox)
https://pdfbox.apache.org/docs/2.0.2/javadocs/org/apache/pdfbox/pdmodel/PDPage.html#setCropBox(org.apache.pdfbox.pdmodel.common.PDRectangle)
https://pdfbox.apache.org/docs/2.0.2/javadocs/org/apache/pdfbox/pdmodel/common/PDRectangle.html
The only thing I'm missing here is how the user can decide to which point he wants to crop (and I'm thinking of a mouse selection & so having the view of the PDF to select which area to crop), otherwise if he inputs random values, it won't be accurate.
package uk.mushow.pdftoexcel;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import java.io.File;
import java.io.IOException;
public class PDFToExcel extends Application {
public static void main(String[] args) {
launch();
}
#Override
public void start(final Stage stage) {
stage.setTitle("PDF Test");
final FileChooser fileChooser = new FileChooser();
final Button openButton = new Button("Select pdf");
openButton.setOnAction(event -> {
configureFileChooser(fileChooser);
File file = fileChooser.showOpenDialog(stage);
if (file != null) {
try {
openFile(file);
} catch (IOException e) {
e.printStackTrace();
}
}
});
final GridPane inputGridPane = new GridPane();
GridPane.setConstraints(openButton, 0, 1);
inputGridPane.setHgap(6);
inputGridPane.setVgap(6);
inputGridPane.getChildren().add(openButton);
final Pane rootGroup = new VBox(60);
rootGroup.getChildren().add(inputGridPane);
rootGroup.setPadding(new Insets(60, 60, 60, 60));
stage.setScene(new Scene(rootGroup));
stage.show();
}
private void configureFileChooser(final FileChooser fileChooser) {
fileChooser.setTitle("pdf selector");
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PDF", "*.pdf"));
}
private void openFile(File file) throws IOException {
PDDocument pdDocument = PDDocument.load(file);
//This is where I don't know how to handle the file
PDPage pdPage = pdDocument.getPage(0);
//Need the user to select his own cropbox but can't be just values from his head otherwise it wouldn't be accurate);
}
}
Thanks!
EDIT: ADDED IMAGE
How would a user know the coordinates to select that green rectangle?

The best method for getting co-ordinates in most pdf view/annotators is by adding a highlight. and that usually provides some user feed back so the top left of this rescaled example is 45 mm from the left and 111.7mm down from the top (however that alone is not the full picture since the real units have been converted to mm so you need to export the highlight data into a different set of units, which a different viewer may use. Both are corrrect but just showing the values via different outputs.
You need to "Square" up those differences and an export of comments usually does that, however the maths can be a challenge as its done in PDF page co-ordinates.
"rect": [
227.495148,
351.695496,
386.449585,
430.445496
],
"subtype": "Popup",
"annotationType": 16,
"parentType": "Square",
"parentId": "6R",
"parentRect": [
128.322571,
24.175232,
210.179276,
485.779327
],
You will need to experiment with your chosen libraries as to which is easiest, personally for my uses, I would look towards milking command line utilities to do the maths and text output for me.

Related

JavaFX WebView: ignoring setPickOnBounds?

I'm attempting to have two transparent WebViews displayed one on top of the other.
They display alright, however, hyperlinks can only clicked for the WebView at the top.
My understanding is that by setting setPickOnBounds(false), clicks on transparent pixels of the top WebView should go through to the bottom WebView. However, it does appear to be work this way, with the top WebView blocking all clicks.
Is there a way to have overlapping WebView with hyperlinks working for both?
Example:
import java.lang.reflect.Field;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.w3c.dom.Document;
public class DoubleWebViews extends Application {
#Override
public void start(Stage primaryStage) {
new WebPage(primaryStage);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
static class WebPage{
WebView webViewBack = new WebView();
WebView webViewFront = new WebView();
public WebPage(Stage mainstage){
setupWebView(webViewBack, "<a href='https://www.google.com'>URL 1</a> can't be clicked!");
setupWebView(webViewFront, "<br><br><br><a href='https://www.google.com'>URL 2</a>");
StackPane root = new StackPane(webViewBack, webViewFront);
root.setPickOnBounds(false);
Scene scene = new Scene(root);
mainstage.setScene(scene);
mainstage.initStyle(StageStyle.TRANSPARENT);
}
void setupWebView(WebView webView, String html){
webView.setPickOnBounds(false); // doesn't work?
WebEngine webEngine = webView.getEngine();
webEngine.documentProperty().addListener(new DocListener(webEngine));
webEngine.loadContent("<body style='background : rgba(0,0,0,0);font-size: 70px;text-align:center;'>" + html + "</body>");
}
static class DocListener implements ChangeListener<Document>{
private final WebEngine webEngine;
public DocListener(WebEngine webEngine) { this.webEngine = webEngine;}
#Override
public void changed(ObservableValue<? extends Document> observable, Document oldValue, Document newValue) {
try {
// Use reflection to retrieve the WebEngine's private 'page' field.
Field f = this.webEngine.getClass().getDeclaredField("page");
f.setAccessible(true);
com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(this.webEngine);
page.setBackgroundColor((new java.awt.Color(0, 0, 0, 0)).getRGB());
} catch (Exception ignored) {}
}
}
}
}
I've not found an elegant solution yet, but this works.
First, the mouse events received by the front WebView webViewFront need to be forwarded to webViewBack:
webViewFront.addEventFilter(MouseEvent.ANY, event -> webViewBack.fireEvent(event));
This will enable clicks, drags, etc to work on both frames.
As for using the correct cursor, that is a little tricky. Only the cursor of the front WebView is displayed. Therefore, our back WebView must be allowed to modify the front cursor:
webViewBack.cursorProperty().addListener((obs, oldCursor, newCursor) -> webViewFront.setCursor(newCursor));
The problem is now that webViewFront constantly resets its cursor to default if a mouse event occurs and there is no hyperlinks at that location in webViewFront. Therefore, we prevent this reset:
webViewFront.cursorProperty().addListener((obs, oldCursor, newCursor) -> {
if (newCursor != null && "DEFAULT".equals(newCursor.toString())) {
webViewFront.setCursor(webViewBack.getCursor());
}
});
Together, these three changes allow two overlapping WebView with hyperlinks working for both.

How do you do one action through multiple event handlers in Java FX?

I am trying to make a form that does the quadratic formula. It works on command prompt, but I want it to work on a form, and for it to update the output every time you change the input. This is my form so far. There are multiple areas you can change, which are the combo box and the text fields, but I want all of them to update the labels to show an output. This is where my problem is. I do not know how to have something similar to a method that updates the labels. A simplified version of the code is below:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class SmallForm extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
GridPane grid = new GridPane();
grid.setVgap(5);
Label lblConcatenated = new Label("");//label that will hold the concatenated string
grid.add(lblConcatenated, 0, 0);
TextField txtA = new TextField("");//Text field one
grid.add(txtA, 0, 1);
TextField txtB = new TextField("");//Text field two
grid.add(txtB, 0, 2);
txtA.textProperty().addListener(new ChangeListener<String>() { //Triggers whenever the second text field is changed
#Override public void changed(ObservableValue ov, String t, String t1) {
lblConcatenated.setText(txtA.getText() + txtB.getText());//Concatenates the text in the two text fields
}
});
txtB.textProperty().addListener(new ChangeListener<String>() {
#Override public void changed(ObservableValue ov, String t, String t1) { //Triggers whenever the second text field is changed
lblConcatenated.setText(txtA.getText() + txtB.getText());//Concatenates the text in the two text fields
}
});
Scene scene = new Scene(grid, 250, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
}
My goal is to make the concatenation line only have to be written once. This would be a lot more prevalent with more code that was repeated. In command prompt this would be easily done with a method, but that doesn't work in java fx to my knowledge. I've heard the term threading used to do this, but I couldn't figure it out.
Thank you!
You could of course use a methods. You could also use the same ChangeListener<String> object for both properties.
In this case however I recommend using a binding:
lblConcatenated.textProperty().bind(txtA.textProperty().concat(txtB.textProperty()));
If you need to parse the values and do updates based on the parsed values, I recommend using a TextFormatter:
StringConverter<Double> converter = new DoubleStringConverter();
TextFormatter<Double> formatterA = new TextFormatter<>(converter, 0d);
TextFormatter<Double> formatterB = new TextFormatter<>(converter, 0d);
txtA.setTextFormatter(formatterA);
txtB.setTextFormatter(formatterB);
formatterA.valueProperty()...
The formatter values are updated when the TextField looses focus or you trigger the onAction event of the TextField.

How do you Save a ListView as a text document than loading it back into the program

I am currently making a program where you can add and delete items from a listview in Java, i want it to be able to automatically save when you add items to the list view and delete items. I am having a hard time figuring out how to do this any help would be greatly appreciated. i am still very new at programming and still trying to figure it all out here is my code i have so far.
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import javafx.application.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
public class LendingLibraryGUI extends Application {
LendingLibrary LendingLibrary = new LendingLibrary(); //Creating an Object to access total numbers of items
MediaItems Media = new MediaItems(); // creating an array of object to access MediaItems class and allowing it to hold 100 items
private ListView<String> library = new ListView<String>();
ObservableList<String> libraryList = FXCollections.<String>observableArrayList("yes","no");
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane display = new BorderPane(); //Main display
GridPane buttons = new GridPane(); //location to display buttons
TextField outPut = new TextField(); //Text field to show inventory
Insets padding = new Insets(10); //creates Insets for padding
buttons.setPadding(padding); //padding around grid pane
buttons.setHgap(10); //Horizontal gap
library.setItems(libraryList);
for (int i =0; i !=4;i++) { //Loop to create Buttons
String[] actionButtons = {"Add","Check Out","Check In","Delete"};//String to store Button names
Button temp = new Button(actionButtons[i]);
temp.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
buttons.add(temp, i, 0); //add buttons to grid pane
GridPane.setHgrow(temp, Priority.ALWAYS);
GridPane.setVgrow(temp, Priority.ALWAYS);
if (temp.getText().equals("Add")) {
temp.setOnAction((e) -> add());
}
else if (temp.getText().equals("Delete")) {
temp.setOnAction((e) -> deleteLibrary());
}
}
outPut.setEditable(false); //no editing
outPut.setFont(Font.font("monospace", FontWeight.BOLD, 20));
outPut.setMinHeight(300);//sets minimum height
display.setTop(library); //sets output in display on top
display.setCenter(buttons); //sets buttons on center
Scene scene = new Scene(display); //creates new scene
primaryStage.setTitle("Lending Library"); //sets title of GUI
primaryStage.setScene(scene); //adds scene to GUI
primaryStage.setMinHeight(400); //Minimum height
primaryStage.setMinWidth(350);//Minimum Width
primaryStage.show();//Displays GUI to user
}
public static void main(String[] args) {
launch(args);
}
private void add() {
inputGUI("Title:");
}
private void inputGUI(String input) {
Stage secondaryStage = new Stage();
BorderPane border = new BorderPane();
VBox titlePane = new VBox(8);
HBox buttonLayout = new HBox(8);
Label lblTitle = new Label(input);
Button save = new Button("Save");
Button close = new Button("Close");
Insets padding = new Insets(10);
TextField txt = new TextField("");
close.setOnAction((e) -> secondaryStage.close());;
save.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
try {
LendingLibrary.save(library);
} catch (IOException e1) {
e1.printStackTrace();
}
if (txt.getText().trim().isEmpty()) {
}
else {
if (input.equals("Title:")) {
Media.setTitle(txt.getText());
secondaryStage.close();
inputGUI("Format:");
}
else if (input.equals("Format:")) {
Media.setFormat(txt.getText());
secondaryStage.close();
addToLibrary();
}
else if (input.equals("Who did you loan this to?")) {
}
else if (input.equals("When did you loan it(date)?")) {
}
}
}
});
buttonLayout.getChildren().addAll(close,save);
titlePane.setPadding(padding);
titlePane.getChildren().addAll(lblTitle,txt,buttonLayout);
border.setCenter(titlePane);
BorderPane.setAlignment(titlePane, Pos.CENTER);
Scene scene = new Scene(border); //creates new scene
secondaryStage.setTitle("Input"); //sets title of GUI
secondaryStage.setScene(scene); //adds scene to GUI
secondaryStage.setMinHeight(200); //Minimum height
secondaryStage.setMinWidth(350);//Minimum Width
secondaryStage.show();//Displays GUI to user
}
private void addToLibrary() {
String total;
total = Media.getTitle();
total = total + " ("+ Media.getFormat() +")";
libraryList.add(total);
library.setItems(libraryList);
}
private void deleteLibrary() {
int selectedItem = library.getSelectionModel().getSelectedIndex();
libraryList.remove(selectedItem);
}
private void checkOut() {
}
}
Any other pointers or advice would be greatly appreciated!
Thanks in advance
edit:
Again im very new just trying to learn basic stuff this isnt something i am going to keep just going through a book and this is something in it that its trying to teach me.
public void save(ListView<String> library) throws IOException {
File file = new File ("LendingLibrary.txt"); //creates text file
PrintWriter output = new PrintWriter(file);
if(file.exists()) { //if the file exists
output.println(library);
output.close();
}
if(!file.exists()) { //if file doesn't exist
System.out.println("Error creating file");
}
}
What you really are interested to save is the data that is presented by the list view, you don't need all the other layout information and stuff as they are statically defined in the application and loaded on each run automatically.
Now, although saving the data in a file and loading it each time you need it can work, it is not usually the best. A better approach is to use a database to store the data of your application in form of relation entities, in this way you have a safer and a more consistent approach to work with. To get yourself started in the topic, you can go on and consult the official reference.
If you want to first try using the file approach, the advice is to save the data in some structured format which is then easy to save and load, or in more proper words serialize/deserialize. For this purpose you can use the json format to store the data in a file, and you can use gson library for example:
Each row of the list view is an object that contains the data.
Reading: Serialize the list of data to json format using the gson and store each of them in a separate line.
Reading: Load the list of strings and deserialize them to the java class using gson.

How to copy and save file which user chosen with FileChooser in javafx [duplicate]

This question already has answers here:
Standard concise way to copy a file in Java?
(16 answers)
Closed 6 years ago.
I am making program where user can choose image with FileChooser and display it in program. But i want to save all the images in my folder. I want to store all the images there. So is there any option if user choose image which is on desktop to make a copy of that image and paste it in my folder?
Well im not entirely sure about your implementation and how you are presenting the opened image in your program but taking oracles example from here : http://docs.oracle.com/javafx/2/ui_controls/file-chooser.htm
its fairly simple to make the program copy the selected files to a certain direction using javaNIO:
private void openFile(File file) {
try {
File dest = new File("C:\\Users\\yourProfile\\Desktop"); //any location
Files.copy(file.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
Logger.getLogger(
FileChooserSample.class.getName()).log(
Level.SEVERE, null, ex
);
}
}
You can test this with the example application i linked earlier:
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public final class FileChooserSample extends Application {
private Desktop desktop = Desktop.getDesktop();
#Override
public void start(final Stage stage) {
stage.setTitle("File Chooser Sample");
final FileChooser fileChooser = new FileChooser();
final Button openButton = new Button("Open a Picture...");
final Button openMultipleButton = new Button("Open Pictures...");
openButton.setOnAction(
new EventHandler<ActionEvent>() {
#Override
public void handle(final ActionEvent e) {
File file = fileChooser.showOpenDialog(stage);
if (file != null) {
openFile(file);
}
}
});
openMultipleButton.setOnAction(
new EventHandler<ActionEvent>() {
#Override
public void handle(final ActionEvent e) {
List<File> list =
fileChooser.showOpenMultipleDialog(stage);
if (list != null) {
for (File file : list) {
openFile(file);
}
}
}
});
final GridPane inputGridPane = new GridPane();
GridPane.setConstraints(openButton, 0, 0);
GridPane.setConstraints(openMultipleButton, 1, 0);
inputGridPane.setHgap(6);
inputGridPane.setVgap(6);
inputGridPane.getChildren().addAll(openButton, openMultipleButton);
final Pane rootGroup = new VBox(12);
rootGroup.getChildren().addAll(inputGridPane);
rootGroup.setPadding(new Insets(12, 12, 12, 12));
stage.setScene(new Scene(rootGroup));
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
private void openFile(File file) {
try {
desktop.open(file);
File dest = new File("C:\\Users\\yourprofile\\Desktop");
Files.copy(file.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
Logger.getLogger(
FileChooserSample.class.getName()).log(
Level.SEVERE, null, ex
);
}
}
}
You just have to modify the directory, note however there are many ways and implementations of copying files depending on which version of java you are using and whether or not you are using other libraries such as apache io.
Other links that may be useful if you are using a standard file method :
JavaPractices JavaCodeGeek StackOverflow
Hope that helps in anyway :) good luck with your program.

Render GUI to Image in Memory

Is it somehow possible to render a GUI to a BufferedImage or another kind of memory image without displaying it on a screen ?
I know this will loose all kinds of hardware acceleration, but for a simple GUI that is refreshed only once or twice a second this should not be an issue.
Already tried to get JavaFX to output an image, but i can't find a way to leave out rendering on a screen first. Does anyone know a way to do this with JavaFX or Swing ?
It is no problem to draw a simple GUI myself using simple image manipulations, but then i would have to do it all by hand and using Swing or FX would make it much easier.
Edit:
To make it a bit more clear, i don't have an active display, but i can save an image which then gets displayed through other means. To be exact its a raspberry pi, but without a primary display device with a connected tft display using the GPIO port. So i can't render the UI directly to a display device, but need to create an image that i can save at a specific location. All methods i have tried so far need a primary display device.
Yes, it is possible to render a GUI to an image offscreen.
Here is a sample using JavaFX, with example image output as below:
The example works by rendering the chart to an scene which is not added to any window and no window (Stage in JavaFX terminology) is ever shown. The snapshot method is used to take a snapshot of the node and then ImageIO utilities are used to save the snapshot to disk.
Rendering of the offscreen scene will be hardware accelerated if the underlying hardware/software platform supports it.
import javafx.application.*;
import javafx.collections.*;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.*;
import javafx.scene.chart.PieChart;
import javafx.scene.image.Image;
import javafx.scene.layout.Region;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.*;
public class OffscreenImageRecorder extends Application {
private static final Logger logger = Logger.getLogger(OffscreenImageRecorder.class.getName());
private static final String IMAGE_TYPE = "png";
private static final String IMAGE_FILENAME = "image." + IMAGE_TYPE;
private static final String WORKING_DIR = System.getProperty("user.dir");
private static final String IMAGE_PATH = new File(WORKING_DIR, IMAGE_FILENAME).getPath();
private final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
private final Random random = new Random();
private final int CHART_SIZE = 400;
#Override
public void start(Stage stage) throws IOException {
Parent chart = createChart();
Image image = snapshot(chart);
exportPng(SwingFXUtils.fromFXImage(image, null), IMAGE_PATH);
Platform.exit();
}
private Parent createChart() {
// create a chart.
final PieChart chart = new PieChart();
ObservableList<PieChart.Data> pieChartData =
FXCollections.observableArrayList(
new PieChart.Data("Grapefruit", random.nextInt(30)),
new PieChart.Data("Oranges", random.nextInt(30)),
new PieChart.Data("Plums", random.nextInt(30)),
new PieChart.Data("Pears", random.nextInt(30)),
new PieChart.Data("Apples", random.nextInt(30))
);
chart.setData(pieChartData);
chart.setTitle("Imported Fruits - " + dateFormat.format(new Date()));
// It is important for snapshots that the chart is not animated
// otherwise we could get a snapshot of the chart before the
// data display has been animated in.
chart.setAnimated(false);
chart.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
chart.setPrefSize(CHART_SIZE, CHART_SIZE);
chart.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
return chart;
}
private Image snapshot(final Parent sourceNode) {
// Note: if the source node is not in a scene, css styles will not
// be applied during a snapshot which may result in incorrect rendering.
final Scene snapshotScene = new Scene(sourceNode);
return sourceNode.snapshot(
new SnapshotParameters(),
null
);
}
private void exportPng(BufferedImage image, String filename) {
try {
ImageIO.write(image, IMAGE_TYPE, new File(filename));
logger.log(Level.INFO, "Wrote image to: " + filename);
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) {
launch(args);
}
}
It's a bit of a hack, but you could create a frame and position it on a invisible location (using Swing in this example):
frame = new JFrame("Invisible frame");
frame.setBounds(-1000, 100, 640, 480);

Categories

Resources