Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,20 @@ ChartEncoder.saveChart(chart, outputStream, "png");
byte[] bytes = ChartEncoder.getBytes(chart, "png");
```

To choose the output dimensions independently of the chart's construction size, pass an explicit
width and height:

```java
ChartEncoder.saveChart(chart, "./MyChart", "png", 1920, 1080);
ChartEncoder.saveChart(chart, outputStream, "svg", 800, 600);
byte[] bytes = ChartEncoder.getBytes(chart, "png", 1280, 720);
```

Both dimensions must be positive. Raster dimensions are in pixels; vector formats use their
existing rendering units. The chart is laid out at the requested dimensions rather than scaling
an existing image. Rendering follows `IChart.paint`: XChart charts retain the most recently painted
width and height, so later exports without explicit dimensions use that size.

* **Raster formats** (`png`, `jpg`, `bmp`, `gif`, `tiff`) are delegated to `javax.imageio.ImageIO`. Any other format for which an `ImageWriter` plugin is registered on the classpath works too — e.g. add `com.github.usefulness:webp-imageio` to export `webp` (see the `ExampleWebP` demo), or an AVIF plugin to export `avif`. Call `ChartEncoder.getSupportedRasterFormats()` to see what the current classpath can write. For AVIF, where no bundleable ImageIO writer exists yet, the `ExampleAvif` demo shows a portable fallback that pipes a PNG through the `avifenc` CLI (`brew install libavif`).
* **Vector formats** (`svg`, `eps`, `pdf`) require the optional dependencies below.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.knowm.xchart.standalone.issues;

import java.io.IOException;
import org.knowm.xchart.ChartEncoder;
import org.knowm.xchart.SwingWrapper;
import org.knowm.xchart.XYChart;
import org.knowm.xchart.XYChartBuilder;

public class TestForIssue807 {

public static void main(String[] args) throws IOException {
XYChart chart = getChart();
ChartEncoder.saveChart(chart, "chart-1280x720", "png", 1280, 720);
new SwingWrapper<>(chart).displayChart();
}

/** Constructs the chart without launching a window. */
public static XYChart getChart() {
XYChart chart =
new XYChartBuilder()
.width(640)
.height(480)
.title("Export at a chosen size")
.xAxisTitle("Time")
.yAxisTitle("Value")
.build();
chart.addSeries("Sample", new double[] {0, 1, 2, 3, 4}, new double[] {1, 3, 2, 5, 4});
return chart;
}
}
116 changes: 104 additions & 12 deletions xchart/src/main/java/org/knowm/xchart/ChartEncoder.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.knowm.xchart;

import java.awt.Graphics2D;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
Expand All @@ -13,7 +14,8 @@
import org.knowm.xchart.internal.chartpart.IChart;

/**
* Unified, format-agnostic entry point for exporting a {@link org.knowm.xchart.internal.chartpart.IChart}.
* Unified, format-agnostic entry point for exporting a {@link
* org.knowm.xchart.internal.chartpart.IChart}.
*
* <p>A single {@code format} string selects the encoder:
*
Expand All @@ -23,10 +25,10 @@
* <li>{@code "pdf"} &rarr; {@link PdfboxGraphicsEncoder} (optional {@code
* de.rototor.pdfbox:graphics2d})
* <li>any other name &rarr; the raster path, delegating to {@link javax.imageio.ImageIO}. This
* covers the JDK's built-in writers ({@code png}, {@code jpg}/{@code jpeg}, {@code bmp}, {@code
* gif}, {@code tiff}/{@code tif}, {@code wbmp}) plus <em>any</em> additional format for which
* an ImageIO plugin is registered on the classpath (e.g. {@code webp}, {@code avif} via a
* TwelveMonkeys / native plugin).
* covers the JDK's built-in writers ({@code png}, {@code jpg}/{@code jpeg}, {@code bmp},
* {@code gif}, {@code tiff}/{@code tif}, {@code wbmp}) plus <em>any</em> additional format
* for which an ImageIO plugin is registered on the classpath (e.g. {@code webp}, {@code avif}
* via a TwelveMonkeys / native plugin).
* </ul>
*
* <p>Because raster export is delegated to ImageIO's pluggable Service Provider Interface, new
Expand All @@ -47,8 +49,8 @@ private ChartEncoder() {}
*
* @param chart the chart to export
* @param fileName target file name, with or without extension
* @param format the format name, e.g. {@code "png"}, {@code "svg"}, {@code "pdf"}, {@code "tiff"},
* {@code "webp"}
* @param format the format name, e.g. {@code "png"}, {@code "svg"}, {@code "pdf"}, {@code
* "tiff"}, {@code "webp"}
* @throws IOException if writing fails or no encoder is available for the format
*/
public static void saveChart(IChart chart, String fileName, String format) throws IOException {
Expand All @@ -65,8 +67,8 @@ public static void saveChart(IChart chart, String fileName, String format) throw
*
* @param chart the chart to export
* @param out the target stream
* @param format the format name, e.g. {@code "png"}, {@code "svg"}, {@code "pdf"}, {@code "tiff"},
* {@code "webp"}
* @param format the format name, e.g. {@code "png"}, {@code "svg"}, {@code "pdf"}, {@code
* "tiff"}, {@code "webp"}
* @throws IOException if writing fails or no encoder is available for the format
*/
@SuppressWarnings("deprecation")
Expand Down Expand Up @@ -103,6 +105,96 @@ public static byte[] getBytes(IChart chart, String format) throws IOException {
}
}

/**
* Save a chart to a file at an explicit rendering size. The extension is appended if missing.
*
* <p>The chart is laid out at the requested size, not scaled from a previously rendered image. As
* with {@link IChart#paint(Graphics2D, int, int)}, rendering may update the chart's dimensions.
*
* @param chart the chart to export
* @param fileName target file name, with or without extension
* @param format the format name, e.g. {@code "png"}, {@code "svg"}, or {@code "pdf"}
* @param width positive rendering width (pixels for raster formats)
* @param height positive rendering height (pixels for raster formats)
* @throws IllegalArgumentException if either dimension is not positive
* @throws IOException if writing fails or no encoder is available for the format
*/
public static void saveChart(IChart chart, String fileName, String format, int width, int height)
throws IOException {

saveChart(withSize(chart, width, height), fileName, format);
}

/**
* Write a chart to a stream at an explicit rendering size. Does not close the target stream.
*
* <p>The chart is laid out at the requested size, not scaled from a previously rendered image. As
* with {@link IChart#paint(Graphics2D, int, int)}, rendering may update the chart's dimensions.
*
* @param chart the chart to export
* @param out the target stream
* @param format the format name, e.g. {@code "png"}, {@code "svg"}, or {@code "pdf"}
* @param width positive rendering width (pixels for raster formats)
* @param height positive rendering height (pixels for raster formats)
* @throws IllegalArgumentException if either dimension is not positive
* @throws IOException if writing fails or no encoder is available for the format
*/
public static void saveChart(IChart chart, OutputStream out, String format, int width, int height)
throws IOException {

saveChart(withSize(chart, width, height), out, format);
}

/**
* Generate encoded chart bytes at an explicit rendering size.
*
* <p>The chart is laid out at the requested size, not scaled from a previously rendered image. As
* with {@link IChart#paint(Graphics2D, int, int)}, rendering may update the chart's dimensions.
*
* @param chart the chart to export
* @param format the format name
* @param width positive rendering width (pixels for raster formats)
* @param height positive rendering height (pixels for raster formats)
* @return the encoded image bytes
* @throws IllegalArgumentException if either dimension is not positive
* @throws IOException if writing fails or no encoder is available for the format
*/
public static byte[] getBytes(IChart chart, String format, int width, int height)
throws IOException {

return getBytes(withSize(chart, width, height), format);
}

private static IChart withSize(IChart chart, int width, int height) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Width and height must be positive");
}
return new IChart() {
@Override
public void paint(Graphics2D graphics, int paintWidth, int paintHeight) {
chart.paint(graphics, paintWidth, paintHeight);
}

@Override
public int getWidth() {

return width;
}

@Override
public int getHeight() {

return height;
}

@Override
public String getTitle() {

return chart.getTitle();
}
};
}

/** Raster export via ImageIO's pluggable writers. */
private static void saveRaster(IChart chart, OutputStream out, String format) throws IOException {

Expand Down Expand Up @@ -134,9 +226,9 @@ private static void saveRaster(IChart chart, OutputStream out, String format) th
}

/**
* Returns the sorted set of raster format names that can be written on the current classpath. This
* reflects the JDK's built-in writers plus any registered ImageIO plugins, but not the vector
* formats ({@code svg}, {@code eps}, {@code pdf}).
* Returns the sorted set of raster format names that can be written on the current classpath.
* This reflects the JDK's built-in writers plus any registered ImageIO plugins, but not the
* vector formats ({@code svg}, {@code eps}, {@code pdf}).
*
* @return the available raster format names, lower-cased
*/
Expand Down
Loading
Loading