当前位置: 首页 > news >正文

POI自定义形状转png图片

package com.allen.doc.poi.png;import org.apache.poi.xslf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
import org.openxmlformats.schemas.presentationml.x2006.main.impl.CTShapeImpl;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.List;/*** 最佳实战:PPT 自定义形状 转 PNG* 核心:不使用 POI 绘制,纯手绘 100% 还原*/
public class PptxCustShape2PngV3 {// 输出图片大小(按比例)private static final int DEFAULT_WIDTH = 800;public static void main(String[] args) throws Exception {String pptFile = "D:\\java\\my_code\\my-doc\\src\\main\\resources\\自定义图像转png图片.pptx";String outFile = "D:\\java\\my_code\\my-doc\\src\\main\\resources\\shape.png";try (FileInputStream fis = new FileInputStream(pptFile);XMLSlideShow ppt = new XMLSlideShow(fis)) {XSLFSlide slide = ppt.getSlides().get(0);for (XSLFShape shape : slide.getShapes()) {if (isCustomShape(shape)) {BufferedImage img = renderCustomShape((XSLFAutoShape) shape);ImageIO.write(img, "PNG", new FileOutputStream(outFile));System.out.println("✅ 导出成功:" + outFile);break;}}}}// ==========================================// 1. 判断:是不是 自定义形状 (custGeom)// ==========================================public static boolean isCustomShape(XSLFShape shape) {if (!(shape instanceof XSLFAutoShape autoShape)) return false;CTShapeImpl ctShape = (CTShapeImpl) autoShape.getXmlObject();CTShapeProperties spPr = ctShape.getSpPr();return spPr != null && spPr.isSetCustGeom();}// ==========================================// 2. 核心渲染方法(手绘,100% 还原 PPT)// ==========================================public static BufferedImage renderCustomShape(XSLFAutoShape shape) {CTShapeImpl ctShape = (CTShapeImpl) shape.getXmlObject();CTShapeProperties spPr = ctShape.getSpPr();CTCustomGeometry2D custGeom = spPr.getCustGeom();CTPath2D path = custGeom.getPathLst().getPathList().get(0);// XML 原始宽高long pathW = path.getW();long pathH = path.getH();int canvasW = DEFAULT_WIDTH;int canvasH = (int) (pathH * canvasW / pathW);BufferedImage img = new BufferedImage(canvasW, canvasH, BufferedImage.TYPE_INT_ARGB);Graphics2D g2d = img.createGraphics();g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);g2d.setStroke(new BasicStroke(2.0f));// 渐变(XML 红 → 蓝)GradientPaint paint = new GradientPaint(0, canvasH, Color.RED, canvasW, 0, new Color(0, 114, 188));g2d.setPaint(paint);// ==================== 绘制路径 ====================GeneralPath gp = new GeneralPath();Point2D.Double lastPoint = buildPath(gp, path, canvasW, canvasH);g2d.draw(gp);// ==================== 绘制箭头 ====================
        drawArrowIfPresent(spPr, g2d, lastPoint);g2d.dispose();return img;}// ==========================================// 3. 从 XML 构建路径(moveTo + 所有贝塞尔曲线)// ==========================================private static Point2D.Double buildPath(GeneralPath gp, CTPath2D path, int canvasW, int canvasH) {long pathW = path.getW();long pathH = path.getH();Point2D.Double last = null;// moveToCTPath2DMoveTo moveTo = path.getMoveToList().get(0);CTAdjPoint2D p0 = moveTo.getPt();double x0 = (long)p0.getX() * 1.0 * canvasW / pathW;double y0 =  (long)p0.getY() * 1.0 * canvasH / pathH;gp.moveTo(x0, y0);last = new Point2D.Double(x0, y0);// 所有三次贝塞尔for (CTPath2DCubicBezierTo bez : path.getCubicBezToList()) {List<CTAdjPoint2D> pts = bez.getPtList();CTAdjPoint2D c1 = pts.get(0);CTAdjPoint2D c2 = pts.get(1);CTAdjPoint2D to = pts.get(2);double x1 =  (long)c1.getX() * 1.0 * canvasW / pathW;double y1 =  (long)c1.getY() * 1.0 * canvasH / pathH;double x2 =  (long)c2.getX() * 1.0 * canvasW / pathW;double y2 =  (long)c2.getY() * 1.0 * canvasH / pathH;double x3 =  (long)to.getX() * 1.0 * canvasW / pathW;double y3 =  (long)to.getY() * 1.0 * canvasH / pathH;gp.curveTo(x1, y1, x2, y2, x3, y3);last = new Point2D.Double(x3, y3);}return last;}// ==========================================// 4. 如果 XML 有箭头,就画// ==========================================private static void drawArrowIfPresent(CTShapeProperties spPr, Graphics2D g2d, Point2D.Double end) {CTLineProperties ln = spPr.getLn();if (ln == null || !ln.isSetTailEnd()) return;CTLineEndProperties tailEnd = ln.getTailEnd();if (tailEnd.getType() == STLineEndType.TRIANGLE) {int size = 10;int[] x = {(int) end.x, (int) (end.x - size), (int) (end.x - size)};int[] y = {(int) end.y, (int) (end.y - size/2), (int) (end.y + size/2)};g2d.fillPolygon(x, y, 3);}}
}

 

http://www.jsqmd.com/news/658577/

相关文章:

  • 【FPGA】Vivado综合进程异常终止(PID Not Specified)排查与修复指南
  • 职业发展故事:测试专家成长访谈
  • 手把手教你为i.MX6ULL开发板驱动1.3寸ST7789 TFT屏(附完整设备树与驱动代码)
  • 告别网络卡顿!实测3G都能秒读身份证的Android NFC SDK集成指南(附完整源码)
  • 1TB流量可支撑多少订单数据
  • 从Jar包到实战:手把手教你用Java GDAL读取无人机影像的宽高和坐标系
  • FanControl终极指南:5分钟掌握Windows风扇控制,打造静音高效散热系统
  • iforgeAI再次升级:更强大的 AI 数字团队来了!
  • 从Wi-Fi到5G:聊聊QAM调制为啥成了现代通信的‘扛把子’(附与PSK的性能对比)
  • EMC入门:硬件工程师必须掌握的接地与屏蔽技巧
  • 5分钟快速上手:YuukiPS Launcher - 动漫游戏智能启动器终极指南
  • Qt 倒计时功能从入门到弃坑:一个老码农的实战笔记
  • ANSYS APDL谐响应分析实战:悬臂梁频响函数的MATLAB后处理与可视化
  • 视觉大模型技术演进全景:从Transformer到产业落地实践
  • 别再死记MobileNetV1结构了!用PyTorch手把手拆解Depthwise Separable Conv(附代码)
  • 04-07-07 结构化分析问题 - 学习笔记
  • 不懂 ECharts 也能做大屏?AK-Design 开源低代码,拖拽可视化直接上线,告别手写配置,ECharts 图表一键生成
  • 2025届必备的十大降重复率助手推荐
  • OpenAI 正式推出 GPT-5.4-Cyber:网络安全专属 AI 模型新突破
  • 配置爆炸危机预警!SITS2026最新数据:单系统平均配置项达2143+,AI生成方案已成P0级技术刚需——立即获取首批200个预训练领域模型访问权限
  • iOS Widget透明组件精准适配:从尺寸计算到位置布局的实战指南
  • Linux配置SSH密钥实现安全免密服务器登录
  • NPJ Precis Oncol 加拿大蒙特利尔大学医院研究中心:多组学融合网络预测结直肠癌肝转移术后早期复发
  • 终极指南:用Windhawk轻松实现Windows系统模块化定制
  • “生成即上线”时代已来:如何用轻量级RAG+符号执行实现毫秒级错误定位与自愈?——2024最新实践报告
  • 为什么电机控制观测器要使用锁相环(PLL)---学习笔记
  • 开发卡片新建卡片
  • KMS激活全攻略:5分钟搞定Windows和Office永久激活难题
  • 相控阵天线(二):从阵列因子到波束赋形实战(栅瓣抑制、加权优化与Python仿真)
  • python reno