10 Replies Latest reply: Jul 16, 2012 5:57 PM by jsmith RSS

    LineChart in PDF with IText

    925539
      Hello.
      I'm currently developing an application that generates a chart from temperatures.
      I need to generate a printing page A4 size, and want to include the chart in the middle of the page.
      Can do that taking a screen capture with java.awt.Robot and putting as an image, but that is not for
      production basis. Can anyone help me on this? My main goal is taking the chart and putting it in the middle
      of a PDF for printing.
      Thanks for your attention.
        • 1. Re: LineChart in PDF with IText
          MiPa
          With the latest preview release you can use the snapshot feature to create an image of your chart, convert that to a BufferedImage and you can then embed that into a PDF file via iText. You'll need the BufferedImage to either be able to write your image to a file and include that in the PDF or use the iText API directly to put the BufferedImage in there.
          • 2. Re: LineChart in PDF with IText
            925539
            But how could I do that capturing without depending on a new Stage being created for the Chart so to have a good image?
            Is there a way to put that LineChart in a Image object without a screen capture?
            • 3. Re: LineChart in PDF with IText
              MiPa
              >
              But how could I do that capturing without depending on a new Stage being created for the Chart so to have a good image?
              Is there a way to put that LineChart in a Image object without a screen capture?
              Yes - take a look at the snapshot method of the node class. (JavaFX 2.2 preview only !!!)
              • 4. Re: LineChart in PDF with IText
                jsmith
                I tried MiPa's recommendation of the snapshot (as he notes - it is not related to a screen capture) and it worked fine.
                import java.io.File;
                import java.io.IOException;
                import javafx.application.Application;
                import javafx.collections.*;
                import javafx.embed.swing.SwingFXUtils;
                import javafx.scene.*;
                import javafx.stage.Stage;
                import javafx.scene.chart.*;
                import javafx.scene.control.Label;
                import javafx.scene.image.*;
                import javafx.scene.layout.*;
                import javafx.scene.paint.Color;
                import javax.imageio.ImageIO;
                
                public class PieChartExport extends Application {
                  @Override public void start(Stage stage) throws IOException {
                    // create a chart.
                    ObservableList<PieChart.Data> pieChartData =
                      FXCollections.observableArrayList(
                        new PieChart.Data("Grapefruit", 13),
                        new PieChart.Data("Oranges", 25),
                        new PieChart.Data("Plums", 10),
                        new PieChart.Data("Pears", 22),
                        new PieChart.Data("Apples", 30)
                      );
                    final PieChart chart = new PieChart(pieChartData);
                    chart.setTitle("Imported Fruits");
                
                    // render an image of the chart to a file.
                    Pane chartContainer = new Pane();
                    chartContainer.getChildren().add(chart);
                    chart.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
                    chart.setPrefSize(2000, 2000);
                    chart.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
                    chart.setStyle("-fx-font-size: 36px;");
                    Scene snapshotScene = new Scene(chartContainer); 
                    SnapshotParameters params = new SnapshotParameters();
                    params.setFill(Color.ALICEBLUE);
                    Image image = chartContainer.snapshot(params, null);
                    File file = new File("chart.png");
                    ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);
                
                    // layout the scene.
                    VBox layout = new VBox(5);
                    layout.getChildren().addAll(
                      new Label("Wrote: " + file.getCanonicalPath()),
                      new ImageView(new Image(file.toURI().toString(), 400, 0, true, true))
                    );
                    layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
                
                    // show the scene.
                    stage.setScene(new Scene(layout));
                    stage.show();
                  }
                  
                  public static void main(String[] args) { launch(args); }
                }
                Edited by: jsmith on Jun 15, 2012 5:19 PM

                My first attempt at this failed, so I filed: http://javafx-jira.kenai.com/browse/RT-22558 "PieChart snapshot renders incorrectly"

                Kevin commented:
                This is a known limitation in CSS and layout processing. In order for either to work correctly for Node.snapshot(), the node in question must be attached to a Scene. The scene need not be attached to a Stage, so the following is sufficient, added any time after the chartContainer is constructed and before snapshot is called: Scene snapshotScene = new Scene(chartContainer);
                As part of finishing the API docs for snapshot, this limitation will be documented.
                Once I updated my code to construct a Scene for the node for which the snapshot was being taken, everything worked fine.
                I edited this post to include the working code.

                Note that once you save the image to a png, the 2000x2000 pixel image (at least in the case of this piechart), compresses really well (down to 164kb) - which should be fine to embed into a fairly compact and portable pdf.
                • 5. Re: LineChart in PDF with IText
                  925539
                  Hi. Thanks for the help.
                  I can achive what i need with your help, but my chart .png comes out without the labels from the axis.
                  Very strange, tried some things but no success.
                  The code that takes the snapshot:
                  public static void execute(ObservableList<Data<String, Double>> listData) throws IOException {                     
                          ObservableList<Series<String, Double>> listSerie 
                                  = FXCollections.observableArrayList();                        
                          
                          Series<String, Double> serie = new Series("Temperatura", listData);        
                          listSerie.add(serie);
                          NumberAxis y = new NumberAxis();
                          CategoryAxis x = new CategoryAxis();
                          LineChart chart = new LineChart(x, y, listSerie);        
                                  
                          Pane chartContainer = new Pane();
                          chartContainer.getChildren().add(chart);
                          chart.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
                          chart.setPrefSize(1600, 1200);
                          chart.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
                          chart.setStyle("-fx-font-size: 36px;");   
                          Scene snapshotScene = new Scene(chartContainer);
                          
                          Image image = chart.snapshot(null, null);
                          File file = new File("chart.png");
                          ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);                                        
                      }
                  As you can see, i create a new chart with the same data. The axis are created the
                  same way on both charts of the app. If i use the same chart, the first one gets vanished from
                  the scene.

                  This link takes you to a snapshot of my real application:
                  https://dl.dropbox.com/u/21110079/Img/help-grafico.png

                  This one is from the chart snapshot itself:
                  https://dl.dropbox.com/u/21110079/Img/errado.png

                  Maybe i'm doing the wrong way.
                  Can help me with that!?
                  • 6. Re: LineChart in PDF with IText
                    jsmith
                    Maybe i'm doing the wrong way.
                    No I don't think so.
                    Can help me with that!?
                    I think this is caused by a bug in the JavaFX runtime.
                    I created jira issue: http://javafx-jira.kenai.com/browse/RT-22736 "LineChart snapshot render incorrectly" so that you can track it - you can vote for or comment on the issue if you like.
                    • 7. Re: LineChart in PDF with IText
                      925539
                      Voted, appreciate your time and concern.
                      Now let me ask you:
                      While that's being worked out, is there another way you can think for
                      my app print a page with that chart in the middle?

                      Edited by: alan-r on 20/06/2012 13:16
                      • 8. Re: LineChart in PDF with IText
                        jsmith
                        is there another way you can think for my app print a page with that chart in the middle?
                        no
                        • 9. Re: LineChart in PDF with IText
                          925539
                          OK, just to close the topic and maybe help someone that tries to do the same thing.
                          I was able to sort it out with some improv.
                          First, if i grab take a snapshot of the LineChart from the scene, everything is ok with the picture, the two axis
                          come along just fine, but the chart would vanish from the scene, leaving the screen partially empty.
                          So i just instantiate a new chart with the same data and setContent(newChart) inside the AnchorPane.
                          That way i have a nice pdf with the chart int he middle. The scene suffered a quick flash, with the chart recreation.
                          But no problem with that, it's ok.
                          One of the requirements of the app is that the user must be able to edit the values of the chart and print, slightly modified, just
                          to round some pikes, and i was able to provide a live editing, allowing them to see the chart go up and down. The table and the chart
                          share the same list of Data items, and providing a proper cellValueFactory does the trick.
                          Thanks for the help.
                          • 10. Re: LineChart in PDF with IText
                            jsmith
                            Quick update on one of the issues raised in this thread.

                            jira issue: http://javafx-jira.kenai.com/browse/RT-22736 "LineChart snapshot render incorrectly"
                            was resolved as Not an issue.

                            Comment by Kevin Rushforth from the jira resolution:
                            This is not a bug. By default, charts will animate the tick marks and labels on the axis, and a variety of other things over a period of time (up to 700 msec). So at the time the chart is created, the text labels are not yet created and won't be until after animation has run. We should document this limitation, though (I will add a note to RT-21572).

                            There are two possible approaches that an application can take:

                            1) set the animated property on the X and Y axes and the chart to false.

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

                            2) Wait for at least 700 msec for the animation to finish before taking the snapshot.