Hot questions for Using PDFBox in javafx

Question:

So I have this scatter diagramm:

final NumberAxis xAxis = new NumberAxis(0, 10, 1);
final NumberAxis yAxis = new NumberAxis(-100, 500, 100);
final ScatterChart<Number, Number> sc = new ScatterChart<Number, Number>(xAxis, yAxis);
xAxis.setLabel("Age (years)");
yAxis.setLabel("Returns to date");
sc.setTitle("Investment Overview");

XYChart.Series series1 = new XYChart.Series();
series1.setName("Equities");
series1.getData().add(new XYChart.Data(4.2, 193.2));
series1.getData().add(new XYChart.Data(2.8, 33.6));
series1.getData().add(new XYChart.Data(6.2, 24.8));
series1.getData().add(new XYChart.Data(1, 14));
series1.getData().add(new XYChart.Data(1.2, 26.4));
series1.getData().add(new XYChart.Data(4.4, 114.4));
series1.getData().add(new XYChart.Data(8.5, 323));
series1.getData().add(new XYChart.Data(6.9, 289.8));
series1.getData().add(new XYChart.Data(9.9, 287.1));
series1.getData().add(new XYChart.Data(0.9, -9));
series1.getData().add(new XYChart.Data(3.2, 150.8));
series1.getData().add(new XYChart.Data(4.8, 20.8));
series1.getData().add(new XYChart.Data(7.3, -42.3));
series1.getData().add(new XYChart.Data(1.8, 81.4));
series1.getData().add(new XYChart.Data(7.3, 110.3));
series1.getData().add(new XYChart.Data(2.7, 41.2));

XYChart.Series series2 = new XYChart.Series();
series2.setName("Mutual funds");
series2.getData().add(new XYChart.Data(5.2, 229.2));
series2.getData().add(new XYChart.Data(2.4, 37.6));
series2.getData().add(new XYChart.Data(3.2, 49.8));
series2.getData().add(new XYChart.Data(1.8, 134));
series2.getData().add(new XYChart.Data(3.2, 236.2));
series2.getData().add(new XYChart.Data(7.4, 114.1));
series2.getData().add(new XYChart.Data(3.5, 323));
series2.getData().add(new XYChart.Data(9.3, 29.9));
series2.getData().add(new XYChart.Data(8.1, 287.4));

sc.getData().addAll(series1, series2);
Scene scene = new Scene(sc, 500, 400);
Stage stage = (Stage) this.bttn_LoadCSVFolder.getScene().getWindow();

Whitch looks like this:

Using this code I managed to save the diagramm to a PDF:

WritableImage image = sc.snapshot(new SnapshotParameters(), null);
BufferedImage awtImage = SwingFXUtils.fromFXImage(image, null);
PDImageXObject pdImageXObject = LosslessFactory.createFromImage(doc, awtImage);
PDPageContentStream contentStream = new PDPageContentStream(doc, doc.getPage(0), PDPageContentStream.AppendMode.APPEND, true, true);
contentStream.drawImage(pdImageXObject, 100, 160, awtImage.getWidth() / 2, awtImage.getHeight() / 2);
contentStream.close();

BUT when I save the chart to my pdf (or even just as a image) the x and y values of the axsis wont be shown:


Answer:

Add this to your code:

xAxis.setAnimated(false); 
yAxis.setAnimated(false);

Reason: "This is because labels are animated (fade-in), so they are not visible on the first frame (which you captured)." (Source)

If your image looks blurry, see this answer.

Question:

I am creating desktop JavaFX application for viewing PDF files. PDFs are at resource folder. I read resourse file as stream, then I create temporary file and use it to convert contents to image and show into ImageView.

       currentPdf =  new File("current.pdf");
        if (!currentPdf.exists()) {
            // In JAR
            InputStream inputStream = ClassLoader.getSystemClassLoader()
                    .getResourceAsStream("PDFSample.pdf");
            // Copy file
            OutputStream outputStream;
            try {
                outputStream = new FileOutputStream(currentPdf);
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
            byte[] buffer = new byte[1024];
            int length;
            try {
                while ((length = inputStream.read(buffer)) > 0) {
                    outputStream.write(buffer, 0, length);
                }
                outputStream.close();
                inputStream.close();
            } catch(IOException e) {
                throw new RuntimeException(e);
            }
        }

The problem is: when I create regular file with

currentPdf =  new File("current.pdf");

Everything works as expected (i get current.pdf created at directory where jar is located). But I want the file to be created at system temp folder and to be deleted on exit from application. I tried this:

try {
    currentPdf =  File.createTempFile("current",".pdf");
} catch (IOException e) {
    throw new RuntimeException(e);
}
currentPdf.deleteOnExit();//also tried to comment this line

And get exception:

Caused by: java.io.IOException: Error: End-of-File, expected line
    at org.apache.pdfbox.pdfparser.BaseParser.readLine(BaseParser.java:1517)
    at org.apache.pdfbox.pdfparser.PDFParser.parseHeader(PDFParser.java:360)
    at org.apache.pdfbox.pdfparser.PDFParser.parse(PDFParser.java:186)
    at org.apache.pdfbox.pdmodel.PDDocument.load(PDDocument.java:1227)
    at org.apache.pdfbox.pdmodel.PDDocument.load(PDDocument.java:1194)
    at org.apache.pdfbox.pdmodel.PDDocument.load(PDDocument.java:1165)
    at ua.com.ethereal.pdfquiz.Controller.getPdfPageAsImage(Controller.java:147)

At this method:

@SuppressWarnings("unchecked")
public static Image getPdfPageAsImage(File pdfFile, int pageNum) {
    Image convertedImage;
    try {
        PDDocument document = PDDocument.load(pdfFile);
        List<PDPage> list = document.getDocumentCatalog().getAllPages();
        PDPage page = list.get(pageNum);
        BufferedImage image = page.convertToImage(BufferedImage.TYPE_INT_RGB, 128);
        convertedImage = SwingFXUtils.toFXImage(image, null);
        document.close();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return convertedImage;
}

Would appreciate help in resolving this or pointing me on way to read file directly from jar to avoid creating temporary copies.


Answer:

How do you create the temporary file?

The usual methods make an empty file in the filesystem. That will trip your logic here:

if (!currentPdf.exists()) {

That check is supposedly there to avoid overwriting exiting files, but in this case you have to get rid of it. As it is, you skip your PDF generation code and try to read an empty file.

Question:

When the JFileChooser is called, the file that is saved and the result is not a pdf. To put in better words, I call JFileChooser and save it with some name. Then when i go to that location, the file does not have the .pdf extension, I tried using .getName() and then adding the .pdf extension to it and setting that equal to .getName() but that does not work. How do I fix this?

I made a JavaFX project for this, and the window on there just displays a button that when clicked the method "methodActivate" is called.

package application;


import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;

import java.io.File;
import java.io.IOException;

import javax.swing.JFileChooser;

import org.apache.pdfbox.pdmodel.*;

public class Main extends Application {

    @FXML
    private Button button;

    @Override
    public void start(Stage primaryStage) {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("\\Pdfbox.fxml"));
            Scene scene = new Scene(root,500,300);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }

    @FXML
    public void methodActivate() throws IOException{

         PDDocument doc = 
         new PDDocument();
            doc.addPage(new PDPage());
            boolean bool = false;
            try{
                JFileChooser fileChooser = new JFileChooser();
                  File file = fileChooser.getSelectedFile();

                  doc.save(file);
                  doc.close();
                }
                doc.close();
            } catch (Exception io){
                System.out.println(io);
            }

    }

}

Answer:

If you enter a name in the JFileChooser window like "myPdf" without the .pdf, then it will save it with that name. JFileChooser doesn't append file types automatically, so if that's something you need then you can check for it

public File checkFileName(File file) {
    if (!file.getAbsolutePath().toLowerCase().endsWith(".pdf")) {
        return new File(file.getAbsolutePath() + ".pdf");
    } else {
        return file;
    }
}

See javadoc for more information about JFileChooser

Question:

I have successfully converted an Image to Pdf. My issue is that the pdf is displaying half of the width My Code:

@FXML
private void print() {
    try {
        WritableImage nodeshot = stackPane.snapshot(new SnapshotParameters(), null);
        File file = new File("C:/Users/Andre Kelvin/Desktop/TheNode.png");
        ImageIO.write(SwingFXUtils.fromFXImage(nodeshot, null), "png", file);

        PDDocument doc = new PDDocument();
        PDPage page = new PDPage();
        PDImageXObject pdimage;
        PDPageContentStream content;

        pdimage = PDImageXObject.createFromFile("C:/Users/Andre Kelvin/Desktop/TheNode.png", doc);
        content = new PDPageContentStream(doc, page);
        content.drawImage(pdimage, 0, 0);
        content.close();
        doc.addPage(page);
        doc.save("C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");
        doc.close();
        file.delete();

        //This Line Automatically Opens the user defualt pdf file viewer
        Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + "C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");
    } catch (Exception e) {
    }
}

I have tried getting the root node width and height by using this line:

content.drawImage(pdimage, 0, 0,(float)stackPane.getPrefWidth(),(float)stackPane.getPrefHeight());

and this:

content.drawImage(pdimage, 0, 0,(float)stackPane.getMaxWidth(),(float)stackPane.getMaxHeight());

it will just display a blank white page.

This is the actual image that is Converted to pdf:

And this is the pdf of the image:


Answer:

Neither the preferred size properties nor their min/max counterparts allow you to reliably determine the size of a Region. Those are just indicators and the calculated values may not match. Furthermore the Region may be resized to sizes other than the preferred size. Last but not least those properties may contain special values Region.USE_PREF_SIZE(=Double.NEGATIVE_INFINITY) and Region.USE_COMPUTED_SIZE(=-1) and even do so by default.

If you need to get the size of a node, use the boundsInLocal property:

Bounds bounds = stackPane.getBoundsInLocal();

In this case it's simpler to get the size of the snapshot instead though.

Furthermore the page size of the PDPage may not be large enough to contain the whole image. You need to scale the image instead or change the page size of the PDPage.


Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + "C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");

This can be done platform-independent using the HostServices available via the Application instance.

Example
@Override
public void start(Stage primaryStage) {
    Button button = new Button("print");
    StackPane root = new StackPane(button);
    button.setOnAction(evt -> {
        try {
            WritableImage nodeshot = root.snapshot(new SnapshotParameters(), null);

            // store image in-memory
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            ImageIO.write(SwingFXUtils.fromFXImage(nodeshot, null), "png", output);
            output.close();

            PDDocument doc = new PDDocument();
            PDPage page = new PDPage();
            PDImageXObject pdimage;
            PDPageContentStream content;

            pdimage = PDImageXObject.createFromByteArray(doc, output.toByteArray(), "png");
            content = new PDPageContentStream(doc, page);

            // fit image to media box of page
            PDRectangle box = page.getMediaBox();
            double factor = Math.min(box.getWidth() / nodeshot.getWidth(), box.getHeight() / nodeshot.getHeight());

            float height = (float) (nodeshot.getHeight() * factor);

            // beware of inverted y axis here
            content.drawImage(pdimage, 0, box.getHeight() - height, (float) (nodeshot.getWidth() * factor), height);

            content.close();
            doc.addPage(page);

            File outputFile = new File("C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");

            doc.save(outputFile);
            doc.close();

            getHostServices().showDocument(outputFile.toURI().toString());
        } catch (Exception e) {
        }
    });

    Scene scene = new Scene(root, 300, 300);

    primaryStage.setScene(scene);
    primaryStage.show();
}