Web 應用發展使報表系統從桌面走向瀏覽器。傳統 OS 控件無法適應多元客戶端,伺服器端生成圖片回傳成為主流。本文介紹開源圖表引擎 JFreeChart,涵蓋環境建置、核心架構、柱狀/餅圖實戰、Servlet 輸出、進階熱點圖表 (ImageMap) 互動功能。
jfreechart-0.9.11.jar + jcommon-0.8.6.jar (缺 jcommon 會報莫名錯誤)UnsatisfiedLinkError: initDDraw → VM 參數加 -Dsun.java2d.noddraw=true 關閉 DirectDraw

| 核心類別 | 職責 |
|---|---|
JFreeChart | 圖表根物件,最終表現形式屬性定制 |
ChartFactory | 工廠類,快速建立各類圖表物件 |
XXXXDataset | 資料集介面 (如 CategoryDataset, PieDataset) |
XXXXPlot | 繪圖區域物件,決定圖表風格,需 Axis/Renderer/Dataset |
XXXXAxis | 座標軸處理 (CategoryAxis, ValueAxis, DateAxis) |
XXXXRenderer | 圖形繪製邏輯 (BarRenderer, PieRenderer 等) |
XXXXURLGenerator | 熱點連結生成器 |
XXXXToolTipGenerator | 工具提示生成器 |
⚠️ 架構大量使用工廠方法,擴展新圖表類型較繁瑣;Plot/Axis/Renderer 組合複雜,初學者需熟悉各構造函數參數含義。
public class BarChartDemo {
public static void main(String[] args) throws IOException {
CategoryDataset dataset = getDataSet2(); // 分組資料集
JFreeChart chart = ChartFactory.createBarChart3D(
"水果產量圖", "水果", "產量", dataset,
PlotOrientation.VERTICAL, true, false, false);
FileOutputStream fos = new FileOutputStream("D:\\fruit.jpg");
ChartUtilities.writeChartAsJPEG(fos, 1.0f, chart, 400, 300, null);
fos.close();
}
private static CategoryDataset getDataSet2() {
DefaultCategoryDataset ds = new DefaultCategoryDataset();
ds.addValue(100, "北京", "蘋果"); ds.addValue(100, "上海", "蘋果"); ds.addValue(100, "廣州", "蘋果");
ds.addValue(200, "北京", "梨子"); ds.addValue(200, "上海", "梨子"); ds.addValue(200, "廣州", "梨子");
ds.addValue(300, "北京", "葡萄"); ds.addValue(300, "上海", "葡萄"); ds.addValue(300, "廣州", "葡萄");
ds.addValue(400, "北京", "香蕉"); ds.addValue(400, "上海", "香蕉"); ds.addValue(400, "廣州", "香蕉");
ds.addValue(500, "北京", "荔枝"); ds.addValue(500, "上海", "荔枝"); ds.addValue(500, "廣州", "荔枝");
return ds;
}
}

public class PieChartDemo {
public static void main(String[] args) throws IOException {
DefaultPieDataset data = getDataSet();
JFreeChart chart = ChartFactory.createPie3DChart(
"水果產量圖", data, true, false, false);
// 寫檔同柱狀圖
}
private static DefaultPieDataset getDataSet() {
DefaultPieDataset ds = new DefaultPieDataset();
ds.setValue("蘋果", 100); ds.setValue("梨子", 200);
ds.setValue("葡萄", 300); ds.setValue("香蕉", 400); ds.setValue("荔枝", 500);
return ds;
}
}

public class ChartDemoServlet extends HttpServlet {
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
res.setContentType("image/jpeg");
DefaultPieDataset data = getDataSet();
JFreeChart chart = ChartFactory.createPie3DChart("水果產量圖", data, true, false, false);
ChartUtilities.writeChartAsJPEG(res.getOutputStream(), 1.0f, chart, 400, 300, null);
}
// getDataSet() 同前
}
需求:用戶點擊柱狀圖「蘋果」→ 顯示各地區蘋果產量明細。
HTML 實現需 <map> + <area> 定義熱點區域:
<map name="chartMap">
<area shape="rect" coords="81,15,126,254" href="?series=0&category=100" title="100 = 7,048" onclick="clickChart('100')">
<area shape="rect" coords="143,27,188,255" href="?series=0&category=200" title="200 = 6,721" onclick="clickChart('200')">
...
</map>

ChartRenderingInfo info = new ChartRenderingInfo()PiePlot plot = (PiePlot) chart.getPlot();
plot.setURLGenerator(new StandardPieURLGenerator("detail.jsp"));
plot.setToolTipGenerator(new StandardPieToolTipGenerator());
info:
ChartUtilities.writeChartAsJPEG(fos, 1.0f, chart, 400, 300, info);
PrintWriter pw = new PrintWriter(new FileOutputStream("d:\\fruit.map"));
ChartUtilities.writeImageMap(pw, "chartMap", info);
pw.flush(); pw.close();
生成的 fruit.map 即為 HTML <map> 內容。建議以標籤庫封裝圖片與 Map 名稱關聯,確保一致性。