SVG 作为矢量图形格式,具备**无损缩放、文本可搜索、DOM 可操作、体积小**等优势,适合 Web 报表、打印、响应式大屏。本文提供两条路径:手写 SVG 路径生成饼图/3D 饼图 (零依赖、极致可控) 与 JFreeChart + Apache Batik 导出 SVG (功能全、标准兼容),附完整代码、中文渲染方案与选型建议。
SVG `
M cx cy:移动到圆心。L cx+r cy:画线到起始点 (0° 方向)。A r r 0 largeArcFlag 1 x y:椭圆弧,largeArcFlag = 角度 > 180° ? 1 : 0。Z:闭合回圆心。
public class SvgPieChartGenerator {
private static final String[] PALETTE = {
"#4E79A7", "#F28E2B", "#E15759", "#76B7B2", "#59A14F",
"#EDC949", "#B07AA1", "#FF9DA7", "#9C755F", "#BAB0AC"
};
public static String generate(double[] values, String[] labels, int width, int height) {
double sum = Arrays.stream(values).sum();
double[] percents = Arrays.stream(values).map(v -> v / sum * 100).toArray();
int cx = width / 2;
int cy = height / 2;
int radius = Math.min(cx, cy) - 20;
int legendX = cx + radius + 30;
int legendY = cy - radius;
StringBuilder svg = new StringBuilder();
svg.append(String.format("\n"));
svg.append(String.format("");
return svg.toString();
}
}
3D 效果 = **顶面椭圆 + 侧面路径 + 底面椭圆** (或仅顶面 + 侧面)。核心参数:
rx (水平半轴)、ry (垂直半轴,ry < rx 形成透视)。depth (厚度),侧面高度。代码量较大,建议直接用 JFreeChart 3D 渲染器再导出 SVG。
<dependencies>
<dependency><groupId>org.jfree</groupId><artifactId>jfreechart</artifactId><version>1.5.4</version></dependency>
<dependency><groupId>org.apache.xmlgraphics</groupId><artifactId>batik-transcoder</artifactId><version>1.16</version></dependency>
<!-- 中文字体支持:确保服务器安装 Microsoft YaHei / SimHei / Noto Sans CJK -->
</dependencies>
public class JFreeChartSvgExporter {
public static void exportPieChart(String filePath, String title, String[] keys, double[] values) throws IOException {
// 1. 构建数据集
DefaultPieDataset dataset = new DefaultPieDataset();
for (int i = 0; i < keys.length; i++) dataset.setValue(keys[i], values[i]);
// 2. 创建图表
JFreeChart chart = ChartFactory.createPieChart(
title, dataset, true, true, false
);
// 中文标题/图例字体
chart.getTitle().setFont(new Font("Microsoft YaHei", Font.BOLD, 18));
chart.getLegend().setItemFont(new Font("Microsoft YaHei", Font.PLAIN, 12));
// 避免饼图标签重叠
PiePlot plot = (PiePlot) chart.getPlot();
plot.setLabelFont(new Font("Microsoft YaHei", Font.PLAIN, 11));
plot.setSimpleLabels(true); // 仅显示 {0}={1}
// 3. 导出 SVG
int width = 800, height = 600;
Rectangle bounds = new Rectangle(0, 0, width, height);
DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
Document document = domImpl.createDocument(null, "svg", null);
SVGGraphics2D g2 = new SVGGraphics2D(document);
chart.draw(g2, bounds);
try (OutputStream os = new FileOutputStream(filePath);
Writer writer = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
g2.stream(writer, true); // true = use CSS
}
}
public static void exportBarChart(String filePath, String title, String categoryLabel, String valueLabel,
String[] seriesKeys, String[] categoryKeys, double[][] data) throws IOException {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
for (int s = 0; s < seriesKeys.length; s++) {
for (int c = 0; c < categoryKeys.length; c++) {
dataset.addValue(data[s][c], seriesKeys[s], categoryKeys[c]);
}
}
JFreeChart chart = ChartFactory.createBarChart3D(title, categoryLabel, valueLabel, dataset,
PlotOrientation.VERTICAL, true, true, false);
// 字体设置同饼图
exportChartToSvg(chart, filePath, 800, 600);
}
private static void exportChartToSvg(JFreeChart chart, String filePath, int w, int h) throws IOException {
Rectangle bounds = new Rectangle(0, 0, w, h);
DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
Document document = domImpl.createDocument(null, "svg", null);
SVGGraphics2D g2 = new SVGGraphics2D(document);
chart.draw(g2, bounds);
try (OutputStream os = new FileOutputStream(filePath);
Writer writer = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
g2.stream(writer, true);
}
}
}
apt-get install fonts-noto-cjk 或 yum install google-noto-sans-cjk-fonts。Font 对象指定字体名,需与系统安装字体名一致 (Noto Sans CJK SC / Microsoft YaHei / SimHei)。Font API,需服务器装字体 |
| 性能 | **极快** (字符串拼接) | 中等 (对象模型 + 渲染管线) |
| 维护成本 | 低 (几百行自控代码) | 低 (成熟库,配置即用) |
| 推荐场景 | **微服务/Serverless/嵌入式/极简饼图** | **企业级报表/多图表类型/标准合规/导出 PDF/PNG 复用** |
gzip / brotli,SVG 文本压缩率极高 (通常 > 80%)。SVGO / svgo-loader)。width="100%" height="100%" viewBox="0 0 800 600" preserveAspectRatio="xMidYMid meet"。<div id="chart"></div> + fetch('/api/chart.svg').then(r=>r.text()).then(svg=>document.getElementById('chart').innerHTML=svg)。掌握双路径,即能在「极简内部工具」与「企业级报表系统」间游刃有余地交付矢量图表能力。