How to print on printer image using javafx8 - java

I have object of javafx.scene.image.Image class. How can I print it on printer using javafx8? Please, note, that I don't want to print some node, for example ImageView. I need to print image. Although it's very simple question I can't find answer in internet.
The only code I found is:
PrinterJob job = PrinterJob.createPrinterJob();
if (job != null) {
boolean success = job.printPage(node);
if (success) {
job.endJob();
}
}
However it is about printing the node.

Problem
javafx.print.PrinterJob only prints Node and it's subclasses. Image isn't a subclass of Node. So you have to wrap it in a Node (ImageView) or print from plain Java.
Difference of JavaFX-PrinterJob and AWT-PrinterJob
The main difference is, that the JavaFX PrinterJob was introduced for usage with Node objects. It has set some usefull things as a JavaFX Property like the Jobstatus or the Printer itself. And it is more thread safe as the older AWT PrinterJob. The AWT PrinterJob can print mostly anything you want like Strings, Images, Arc's, etc., because it takes an AWT Graphics Object to draw things on the page.
Solution
Before you can use the plain Java solution, you have to convert your FX-Image to a BufferedImage with SwingFXUtils.fromFXImage(). But there is a bug with *.jpg Files, as described here: https://stackoverflow.com/a/30995307/4170073
The Minimal, Complete, and Verifiable example down below shows a working solution:
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;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ImagePrinter extends Application {
#Override
public void start(Stage primaryStage) {
Image image = new Image("http://www.gnu.org/graphics/gnu-head.png");
BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);
Button btn = new Button();
btn.setText("Print Image");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
printImage(bufferedImage);
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Image Printer");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void printImage(BufferedImage image) {
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(new Printable() {
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
// Get the upper left corner that it printable
int x = (int) Math.ceil(pageFormat.getImageableX());
int y = (int) Math.ceil(pageFormat.getImageableY());
if (pageIndex != 0) {
return NO_SUCH_PAGE;
}
graphics.drawImage(image, x, y, image.getWidth(), image.getHeight(), null);
return PAGE_EXISTS;
}
});
try {
printJob.print();
} catch (PrinterException e1) {
e1.printStackTrace();
}
}
}

Related

JavaFX loading system icons in a background thread

I have a file browser component in my app (TableView), and I load the icons of files via FileSystemView.getFileSystemView().getSystemIcon(). Now, this is quite slow as I also need to convert the icon to BufferedImage, and that to JavaFX Image. So I needed to push this all to background thread(s). I created an IconLoadingTask, that loads a single icon - and during updateItem() of TableView I push these IconLoadingTasks to ExecutorService. It works, but could be improved.
The problem I have is that if the folder has lots of icons, and the user quickly drags the scrollbar, lots of "redundant" icons are loaded, stalling the thread(s) and resulting in often not seeing the icons that matter at the time: the ones that are currently displayed in the TableView.
Anyone have ideas how to solve this issue? I was thinking of accessing the vertical scrollbar of TableView and listening to it (and perhaps only update after scroll has been released), but I'm not sure how to even access the scrollbar... and maybe there's a simpler solution that escapes me.
EDIT:
Well, well. I created a minimal, complete and verifiable example out of my Kotlin code into Java code, and I cannot reproduce the "effect" anymore. It appears to work now as I intended it to in the first place. So it seems I just have to figure what "extra" I am doing in my main application's code. Anyways, here's the apparently working sample code.
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.filechooser.FileSystemView;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class IconLoadTest extends Application {
public static class FileEntry {
public Path path;
private Image icon;
public FileEntry(Path path) {
this.path = path;
}
synchronized void setIcon(Image icon) {
this.icon = icon;
}
synchronized Image getIcon() {
return icon;
}
public void setPath(Path path) {
this.path = path;
}
public Path getPath() {
return path;
}
}
class MyTableCell extends TableCell<FileEntry, Path> {
#Override
protected void updateItem(Path item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
setGraphic(null);
return;
}
setText(item.getFileName().toString());
Image icon = dataMap.get(item).getIcon();
if (icon == null) {
setGraphic(null);
executor.execute(new IconLoadingTask(item));
} else {
setGraphic(new ImageView(icon));
}
}
}
class IconLoadingTask extends Task<Void> {
private Path path;
IconLoadingTask(Path path) {
this.path = path;
}
#Override
protected Void call() {
FileEntry entry = dataMap.get(path);
if (entry.icon != null) {
return null;
}
entry.setIcon(getIcon(path.toFile()));
// Refresh currently visible items
Platform.runLater(() -> table.refresh());
return null;
}
}
private TableView<FileEntry> table = new TableView<>();
// Table data
private HashMap<Path, FileEntry> dataMap = new HashMap<>();
private List<FileEntry> data = FXCollections.observableArrayList();
private ExecutorService executor = Executors.newFixedThreadPool(1);
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
root.getChildren().add(table);
TableColumn<FileEntry, Path> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<>("path"));
table.getColumns().add(nameCol);
nameCol.setCellFactory(tableColumn -> new MyTableCell());
// Sort so that dirs come first
nameCol.setComparator((o1, o2) -> {
if (Files.isDirectory(o1) && !Files.isDirectory(o2)) {
return -1;
} else if (!Files.isDirectory(o1) && Files.isDirectory(o2)) {
return 1;
} else {
return o1.toString().toLowerCase().compareTo(o2.toString().toLowerCase());
}
});
// Set to a directory with lots of files (e.g. System32 on Windows)
String directory = "C:\\Windows\\System32\\";
// Load files from directory, and create entries for table
Path dir = Paths.get(directory);
List<Path> files = listContents(dir);
for (Path p : files) {
FileEntry entry = new FileEntry(p);
data.add(entry);
dataMap.put(p, entry);
}
table.setItems((ObservableList<FileEntry>) data);
// Sort
table.getSortOrder().add(table.getColumns().get(0));
table.sort();
// Display the app
Scene scene = new Scene(root, 600, 480);
primaryStage.setTitle("Icon Background Loading");
primaryStage.setScene(scene);
primaryStage.show();
}
// Gets directory contents
private static List<Path> listContents(Path directory) {
ArrayList<Path> paths = new ArrayList<>();
try {
Files.newDirectoryStream(directory).forEach(paths::add);
} catch (IOException ex) {
ex.printStackTrace();
}
return paths;
}
// Gets a system icon for a file
private static Image getIcon(File file) {
Icon ico = FileSystemView.getFileSystemView().getSystemIcon(file);
java.awt.Image awtImage = ((ImageIcon) ico).getImage();
BufferedImage bImg;
if (awtImage instanceof BufferedImage) {
bImg = (BufferedImage) awtImage;
} else {
bImg = new BufferedImage(
awtImage.getWidth(null),
awtImage.getHeight(null),
BufferedImage.TYPE_INT_ARGB
);
Graphics graphics = bImg.createGraphics();
graphics.drawImage(awtImage, 0, 0, null);
graphics.dispose();
}
return SwingFXUtils.toFXImage(bImg, null);
}
public static void main(String[] args) {
Application.launch(IconLoadTest.class, args);
}
}

Flood fill using java Pane - JAVA

Hi I am supposed to create function which would do flood fill on a Pane containing Shapes using Java. It is supposed to behave just like MSPaint, I dont need to later move rectangles Lines or other shapes. I was thinking converting Pane into Image and then work with pixels and then clear all Panes children and insert it as a image but I cant make it work.
code example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Paint extends Application {
public static void main(String[] args) {
launch(args);
}
private Pane pane;
#Override
public void start(Stage primaryStage) {
pane= new Pane();
primaryStage.setTitle("Fill");
Scene scene = new Scene(pane,500,600);
primaryStage.setScene(scene);
primaryStage.show();
pane.setOnMousePressed((e)->{
doFill(e.getX(),e.getY());
});
//RECT 1
Rectangle rect1=new Rectangle(1,100,200,300);
rect1.setStroke(Color.BLACK);
rect1.setStrokeWidth(2);
rect1.setFill(Color.WHITE);
//RECT 2
Rectangle rect2=new Rectangle(50,150,200,400);
rect2.setStroke(Color.BLACK);
rect2.setStrokeWidth(2);
rect2.setFill(Color.WHITE);
//LINE
Line line=new Line(0,0,200,550);
rect2.setStroke(Color.BLACK);
rect2.setStrokeWidth(2);
pane.getChildren().addAll(rect1,rect2,line);
}
private void doFill(double eventX, double eventY){
//**TODO**
}
}
Got managed to do that function even though its a bit messy. So for everyone who is getting anxious over this:
private void doFill(double eventX, double eventY,boolean b){
WritableImage i=pane.snapshot(new SnapshotParameters(), null);
ArrayList<Integer> pozicie=new ArrayList<Integer>();
ArrayList<Character> strany=new ArrayList<Character>();
pozicie.add((int)eventX);
pozicie.add((int)eventY);
int c=i.getPixelReader().getColor((int)eventX,(int)eventY).hashCode();
if(c==usedColor.hashCode()){
//System.out.println("same color");
return;}
strany.add('a');
while(pozicie.size()!=0){
char strana=strany.remove(0);
int x=pozicie.remove(0);
int y=pozicie.remove(0);
i.getPixelWriter().setColor(x, y, usedColor);
if(strana=='d'){
//iba dole
if(y<pane.getHeight()-2 && i.getPixelReader().getColor(x, y+1).hashCode()==c){
pozicie.add(x);
pozicie.add(y+1);
strany.add('d');
}
}
else if(strana=='u'){
//iba hore
if( y>100 && i.getPixelReader().getColor(x, y-1).hashCode()==c){
pozicie.add(x);
pozicie.add(y-1);
strany.add('u');
}
}
else{
if(x>2 && i.getPixelReader().getColor(x-1, y).hashCode()==c){
pozicie.add(x-1);
pozicie.add(y);
strany.add('l');
}
if(x<pane.getWidth()-2 && i.getPixelReader().getColor(x+1, y).hashCode()==c){
pozicie.add(x+1);
pozicie.add(y);
strany.add('r');
}
if( y>101 && i.getPixelReader().getColor(x, y-1).hashCode()==c){
pozicie.add(x);
pozicie.add(y-1);
strany.add('u');
}
if(y<pane.getHeight()-2 && i.getPixelReader().getColor(x, y+1).hashCode()==c){
pozicie.add(x);
pozicie.add(y+1);
strany.add('d');
}
}
}
pane.getChildren().clear();
pane.getChildren().add(new ImageView(i));
}

Complete JFXPanel to image

I want to take a snapshot of a website without any video, so it's just plain text with some css and pictures.
I am using a WebView (which is the scene of the JFXPanel) to load the website and then save it via
WritableImage image = scene.snapshot(new WritableImage(1920, 1080));
BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);
ImageIO.write(bufferedImage, "png", file);
(where "scene" is the scene of the JFXPanel)
but the saved image just displays a part of the website, instead of the complete content (see picture).
How do I ensure/enforce that the dimensions of the image matches the dimensions of the JFXPanel content and everthing is visible?
Complete Code:
package renderer;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.embed.swing.JFXPanel;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.WritableImage;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class HtmlRenderer extends Application {
private JFXPanel jfxPanel;
private WebView webView;
public void start(Stage stage) {
jfxPanel = new JFXPanel();
webView = new WebView();
webView.getEngine().getLoadWorker().stateProperty().addListener(
new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState) {
if (newState == Worker.State.SUCCEEDED) {
HtmlRenderer.this.toImage(jfxPanel.getScene());
try {
Platform.exit();
HtmlRenderer.this.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
jfxPanel.setScene(new Scene(webView));
this.updateView("http://www.stackoverflow.com/");
}
private void toImage(Scene scene) {
WritableImage image = scene.snapshot(new WritableImage(1920, 1080));
// TODO: save in matching dir using proper filename
File file = new File("D:/workspace/SiteChecker/test.png");
try {
BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);
ImageIO.write(bufferedImage, "png", file);
} catch (IOException e) {
// TODO: exception handling
}
}
public void updateView(String url) {
webView.getEngine().load(url);
}
private void reloadView() {
webView.getEngine().reload();
}
}
So I found a solution, but it's far from perfect and not really perfomant.
However, nothing else works for me.
The trick is to load the website once, determine width and height of the site. The second time I set the preferred size of the Webview to the determined values and load the website again with the new size. I think it's because the first time only the visible part is rendered.
The width and height can be determined with javascript, e.g.:
private int getPageWidth(WebView webView) {
String script = "Math.max(" +
"document.body.scrollWidth, document.body.offsetWidth," +
"document.documentElement.clientWidth, document.documentElement.scrollWidth," +
"document.documentElement.offsetWidth );";
WebEngine engine = webView.getEngine();
int maxWidth = (int) engine.executeScript(script);
return maxWidth;
}
For some reason some websites have a funny end/bottom i.e. there is a lot of empty space.

Java change Label image

I have two different StackPanes with HBoxes I want to change the image of a label in the second StackPane with a Label(MouseListener) in the first StackPane I think the problem is that the Label doesn't gets repainted or reloaded
First StackPane:
Label label= new Label("",new ImageView(ClearSpace));
label.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
HotBar hb = new HotBar();
if(hb.getX1() == 0){
hb.setImageX1(5);
}
event.consume();
});
Second StackPane(HotBar):
public Label x1;
Image image= new Image(getClass().getResourceAsStream("/resources/images/Test.png"));
...
Label x1 = new Label("",new ImageView(image));
...
public void setImage(int i){
if(i == 5){
x1.setGraphic(new ImageView(image2));
}
}
I think these are the importantst parts of hte code
setImage() is definetly working if you use it below Label x1 = ... it works
In your EventHandler you create a new instance of HotBar on which you do changes, but this instance is not linked to the scene.
Instead you should pass the instance of HotBar into the other class and use that in your event handler.
package helloworld;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
/**
* Created by matt on 3/22/16.
*/
public class SwapLabel extends Application {
int i = 0;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
Image img1 = new Image("http://www.logotemplater.com/freelogostemplates/voope-free-logo-template.png", true);
Image img2 = new Image("http://www.logotemplater.com/freelogostemplates/zoozz-vector-logo-template-sample.png", true);
Label l = new Label("", new ImageView(img1));
l.addEventHandler(MouseEvent.MOUSE_CLICKED, e->{
i = (i+1)%2;
if(i==0){
l.setGraphic(new ImageView(img1));
}else{
l.setGraphic(new ImageView(img2));
}
});
root.getChildren().add(l);
primaryStage.setScene(new Scene(root, 200, 200));
primaryStage.show();
}
}
This appears to be what you are trying to do, but this works. So most likely something else is happening.

JavaFX : How can I set a background image for my stage

I'm writing a code for a bouncing ball program and everything has worked so far but I don't know how to set a background image for my stage. I'm beginner in java (well , obviously) and tried to use ImageView class to set a background for my stage. but no luck so far.
Thank you so much .
package particlesystemexample;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ParticleSystemExample extends Application {
#Override
public void start(Stage primaryStage) {
ParticleAnimationPane paPane = new ParticleAnimationPane();
// mouse actions to pause and resume animation
//paPane.setOnMousePressed(e -> paPane.pause()); // Java 8.0
paPane.setOnMousePressed(new EventHandler<MouseEvent>() {
public void handle(MouseEvent me) {
paPane.init(); }
});
// paPane.setOnMouseReleased(e -> paPane.play()); // Java 8.0
paPane.setOnMouseReleased(new EventHandler<MouseEvent>() {
public void handle(MouseEvent me) {
paPane.play(); }
});
// buttons to increase and decrease animation speed
paPane.setOnKeyPressed(e -> {
if(e.getCode() == KeyCode.UP){
paPane.increaseSpeed();
}
else if (e.getCode() == KeyCode.DOWN){
paPane.decreaseSpeed();
}
});
// Create a scene and place it on stage
Scene scene = new Scene(paPane, 450, 350);
primaryStage.setTitle("Particle System Example");
primaryStage.setScene(scene);
primaryStage.show();
paPane.init(); // initialize the particles
paPane.requestFocus();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Edit :
I tried using this code to display the image but its not working ...
StackPane sp = new StackPane();
Image img = new Image("http://mikecann.co.uk/wp-content/uploads/2009/12/javafx_logo_color_1.jpg");
ImageView imgView = new ImageView(img);
sp.getChildren().add(imgView);
Personally, I use the BufferedImage and Image classes to deal with all of my image needs.
You can use the Image class for this one.
Here is an example:
try {
Graphics.drawImage(ImageIO.read(new File("Picture.png")), fX, fY, fW, fH, sX, sY, sW, sH,null);
} catch (IOException e) {
e.printStackTrace();
}
The things starting with F are the bounds for the rectangle that you will be painting it on.
The things starting with s are the bounds for what part of the picture you are painting.
I'm not exactly sure what the null at the end corresponds to. I just ignore it and everything works out fine.

Categories