怒江傈僳族自治州网站建设_网站建设公司_Vue_seo优化
2025/12/18 0:44:10 网站建设 项目流程
package com.allen.doc.poi;import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xslf.usermodel.*;import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;import static org.apache.poi.openxml4j.opc.TargetMode.INTERNAL;/*** 低版本POI(3.17)终极适配:* 1. XSLFSlideMaster构造器protected → 反射实例化* 2. 无createTheme/createSlideMaster/SlideLayoutType* 3. 完整构建母版-布局-主题-幻灯片关系*/
public class LowVersionPOIFinalBuilder {// 低版本布局类型常量(替代SlideLayoutType)private static final int LAYOUT_TITLE_AND_CONTENT = 1;private static final int LAYOUT_BLANK = 6;private static Integer index = 12;public static void main(String[] args) {XMLSlideShow ppt = new XMLSlideShow();try {// ========== 步骤1:手动创建XSLFTheme(无createTheme()) ==========XSLFTheme customTheme = createTheme(ppt);System.out.println("Theme创建完成:" + customTheme.getPackagePart().getPartName());// ========== 步骤2:反射实例化XSLFSlideMaster(解决protected构造器) ==========XSLFSlideMaster customMaster = createSlideMasterByReflection(ppt, customTheme);System.out.println("母版反射实例化完成:" + customMaster.getPackagePart().getPartName());// ========== 步骤3:手动创建XSLFSlideLayout并绑定母版 ==========XSLFSlideLayout customLayout = createSlideLayout(ppt, customMaster, LAYOUT_TITLE_AND_CONTENT);System.out.println("布局创建完成:" + customLayout.getPackagePart().getPartName());String rId = "rId" + (customMaster.getPackagePart().getRelationships().size() + 1);customMaster.getPackagePart().addRelationship(customLayout.getPackagePart().getPartName(), INTERNAL, XSLFRelation.SLIDE_LAYOUT.getRelation(), rId);// ========== 步骤4:基于自定义布局创建幻灯片 ==========XSLFSlide slide = createSlide(ppt, customLayout);System.out.println("幻灯片创建完成:" + slide.getPackagePart().getPartName());// ========== 保存验证 ==========savePPT(ppt, "final_low_version_ppt.pptx");System.out.println("PPT生成成功:final_low_version_ppt.pptx");} catch (Exception e) {e.printStackTrace();} finally {try {ppt.close();} catch (IOException e) {e.printStackTrace();}}}/*** 步骤1:手动创建XSLFTheme(低版本无createTheme())*/private static XSLFTheme createTheme(XMLSlideShow ppt) throws Exception {OPCPackage opc = ppt.getPackage();// 1. 创建Theme的PackagePart
//        PackagePartName themePartName = PackagingURIHelper.createPartName("/ppt/theme/theme1.xml");PackagePartName themePartName = PackagingURIHelper.createPartName("/ppt/theme/theme" + (ppt.getSlideMasters().size() + 1) + ".xml");PackagePart themePart = opc.createPart(themePartName, XSLFRelation.THEME.getContentType());// 2. 写入极简Theme XML(保证结构合法)String themeXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +"<a:theme xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" name=\"CustomTheme\">" +"<a:themeElements>" +"<a:fontScheme name=\"CustomFont\">" +"<a:majorFont><a:latin typeface=\"微软雅黑\"/></a:majorFont>" +"<a:minorFont><a:latin typeface=\"微软雅黑\"/></a:minorFont>" +"</a:fontScheme>" +"</a:themeElements>" +"</a:theme>";try (OutputStream out = themePart.getOutputStream()) {IOUtils.copy(new ByteArrayInputStream(themeXml.getBytes("UTF-8")), out);}// 3. 实例化XSLFTheme(构造器是public)return new XSLFTheme(themePart);}/*** 步骤2:反射调用XSLFSlideMaster的protected构造器实例化母版* 核心:解决"构造器protected无法new"的问题*/private static XSLFSlideMaster createSlideMasterByReflection(XMLSlideShow ppt, XSLFTheme theme) throws Exception {OPCPackage opc = ppt.getPackage();// 1. 创建母版的PackagePart
//        PackagePartName masterPartName = PackagingURIHelper.createPartName("/ppt/slideMasters/slideMaster1.xml");PackagePartName masterPartName = PackagingURIHelper.createPartName("/ppt/slideMasters/slideMaster" + (ppt.getSlideMasters().size() + 1) + ".xml");PackagePart masterPart = opc.createPart(masterPartName, XSLFRelation.SLIDE_MASTER.getContentType());// 2. 写入母版基础XML(绑定主题关系)String masterXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +"<p:sldMaster xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\">" +"<p:themeOverride>" +"<a:themeElements xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +"<a:fontScheme name=\"\"><a:majorFont><a:latin typeface=\"微软雅黑\"/></a:majorFont>" +"<a:minorFont><a:latin typeface=\"微软雅黑\"/></a:minorFont></a:fontScheme>" +"</a:themeElements>" +"</p:themeOverride>" +"</p:sldMaster>";try (OutputStream out = masterPart.getOutputStream()) {IOUtils.copy(new ByteArrayInputStream(masterXml.getBytes("UTF-8")), out);}// 3. 母版添加主题关系(核心:母版依赖主题)masterPart.addRelationship(theme.getPackagePart().getPartName(),INTERNAL,XSLFRelation.THEME.getRelation(),"rId1");// 4. 反射调用XSLFSlideMaster的protected构造器(关键修复点)// 获取构造器:参数为(PackagePart)Constructor<XSLFSlideMaster> masterConstructor = XSLFSlideMaster.class.getDeclaredConstructor(PackagePart.class);masterConstructor.setAccessible(true); // 绕开protected限制XSLFSlideMaster master = masterConstructor.newInstance(masterPart); // 实例化母版// 5. 将母版加入PPT的母版列表ppt.getSlideMasters().add(master);// 6. 初始化母版的_layouts Map(避免null)initMasterLayoutsMap(master);return master;}/*** 步骤3:手动创建XSLFSlideLayout(低版本无createSlideLayout())*/private static XSLFSlideLayout createSlideLayout(XMLSlideShow ppt, XSLFSlideMaster master, int layoutType) throws Exception {OPCPackage opc = ppt.getPackage();// 1. 生成布局PartName(序号递增)int layoutIdx = master.getSlideLayouts().length + 1;if (layoutIdx < 10) {layoutIdx = index;}index++;PackagePartName layoutPartName = PackagingURIHelper.createPartName("/ppt/slideLayouts/slideLayout" + layoutIdx + ".xml");PackagePart layoutPart = opc.createPart(layoutPartName, XSLFRelation.SLIDE_LAYOUT.getContentType());XSLFSlideLayout[] slideLayouts = new XMLSlideShow().getSlideMasters().get(0).getSlideLayouts();XSLFSlideLayout defaultXSLFSlideLayout = slideLayouts[0];// 2. 写入布局XML(适配标题+内容类型)try (OutputStream out = layoutPart.getOutputStream()) {
//            IOUtils.copy(new ByteArrayInputStream(layoutXml.getBytes("UTF-8")), out);IOUtils.copy(defaultXSLFSlideLayout.getPackagePart().getInputStream(), out);}// 3. 布局添加母版关系String rId = "rId" + (layoutPart.getRelationships().size() + 1);layoutPart.addRelationship(master.getPackagePart().getPartName(),INTERNAL,XSLFRelation.SLIDE_MASTER.getRelation(),rId);// 4. 反射实例化XSLFSlideLayout(构造器同样protected)Constructor<XSLFSlideLayout> layoutConstructor = XSLFSlideLayout.class.getDeclaredConstructor(PackagePart.class);layoutConstructor.setAccessible(true);XSLFSlideLayout layout = layoutConstructor.newInstance(layoutPart);// 5. 反射绑定布局的master字段bindLayoutMaster(layout, master);// 6. 将布局加入母版的_layouts MapMap<String, XSLFSlideLayout> layoutsMap = getMasterLayoutsMap(master);String layoutKey = "slideLayout" + layoutIdx;layoutsMap.put(layoutKey, layout);return layout;}/*** 步骤4:基于自定义布局创建幻灯片*/private static XSLFSlide createSlide(XMLSlideShow ppt, XSLFSlideLayout layout) throws Exception {// 1. 创建空白幻灯片
//        XSLFSlide slide = ppt.createSlide(layout);XSLFSlide slide = ppt.createSlide();// 2. 幻灯片添加布局关系PackagePart slidePart = slide.getPackagePart();slidePart.addRelationship(layout.getPackagePart().getPartName(),INTERNAL,XSLFRelation.SLIDE_LAYOUT.getRelation(),"rId1");// 3. 示例:添加内容验证XSLFTextShape title = slide.createTextBox();title.setAnchor(new java.awt.Rectangle(50, 50, 600, 80));title.setText("低版本POI最终适配演示");XSLFTextShape content = slide.createTextBox();content.setAnchor(new java.awt.Rectangle(50, 150, 600, 300));content.setText("1. 反射调用protected构造器实例化母版\n2. 无新增API依赖\n3. PPT可正常打开");return slide;}// ===================== 工具方法 =====================/*** 初始化母版的_layouts Map(避免null)*/private static void initMasterLayoutsMap(XSLFSlideMaster master) throws Exception {Field layoutsField = XSLFSlideMaster.class.getDeclaredField("_layouts");layoutsField.setAccessible(true);if (layoutsField.get(master) == null) {layoutsField.set(master, new HashMap<String, XSLFSlideLayout>());}}/*** 获取母版的_layouts Map*/private static Map<String, XSLFSlideLayout> getMasterLayoutsMap(XSLFSlideMaster master) throws Exception {Field layoutsField = XSLFSlideMaster.class.getDeclaredField("_layouts");layoutsField.setAccessible(true);return (Map<String, XSLFSlideLayout>) layoutsField.get(master);}/*** 反射绑定布局的master字段*/private static void bindLayoutMaster(XSLFSlideLayout layout, XSLFSlideMaster master) throws Exception {Field masterField = XSLFSlideLayout.class.getDeclaredField("_master");masterField.setAccessible(true);masterField.set(layout, master);}//    /**
//     * 根据布局类型返回XML模板
//     */
//    private static String getLayoutXml(int layoutType) {
//        if (layoutType == LAYOUT_TITLE_AND_CONTENT) {
//            return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
//                    "<p:sldLayout xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\" type=\"titleAndContent\">" +
//                    "<p:cSld><p:spTree>" +
//                    "<p:sp><p:nvSpPr><p:cNvPr id=\"1\" name=\"标题\"/><p:cNvSpPr/></p:nvSpPr>" +
//                    "<p:spPr><a:xfrm xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\"><a:off x=\"50000\" y=\"50000\"/><a:ext cx=\"600000\" cy=\"80000\"/></a:xfrm></p:spPr>" +
//                    "<p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>标题占位符</a:t></a:r></a:p></p:txBody></p:sp>" +
//                    "<p:sp><p:nvSpPr><p:cNvPr id=\"2\" name=\"内容\"/><p:cNvSpPr/></p:nvSpPr>" +
//                    "<p:spPr><a:xfrm><a:off x=\"50000\" y=\"150000\"/><a:ext cx=\"600000\" cy=\"300000\"/></a:xfrm></p:spPr>" +
//                    "<p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>内容占位符</a:t></a:r></a:p></p:txBody></p:sp>" +
//                    "</p:spTree></p:cSld></p:sldLayout>";
//        } else {
//            // 空白布局
//            return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
//                    "<p:sldLayout xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\" type=\"blank\">" +
//                    "<p:cSld/></p:sldLayout>";
//        }
//    }/*** 保存PPT*/private static void savePPT(XMLSlideShow ppt, String path) throws IOException {try (FileOutputStream fos = new FileOutputStream(path)) {ppt.write(fos);}}
}

  

package com.allen.doc.poi;import org.apache.poi.openxml4j.opc.*;
import org.apache.poi.sl.usermodel.Placeholder;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xslf.usermodel.*;import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;import static org.apache.poi.openxml4j.opc.TargetMode.INTERNAL;/*** 修复版:低版本POI(3.17)构建合法PPT,解决打开报错问题* 核心优化:* 1. 修复布局XML流读取异常,使用内置合法模板* 2. 清理重复关系绑定,遵循OPC规范* 3. 规范文件命名/ID生成规则* 4. 修复反射字段绑定、构造器参数错误*/
public class FixedLowVersionPOIBuilder {// 低版本布局类型常量private static final int LAYOUT_TITLE_AND_CONTENT = 1;// 全局计数:保证Layout文件命名连续递增(从1开始)private static int layoutSeq = 12;public static void main(String[] args) {XMLSlideShow ppt = new XMLSlideShow();try {// 步骤1:创建合法Theme(绑定PPT上下文)XSLFTheme customTheme = createValidTheme(ppt);System.out.println("Theme创建完成:" + customTheme.getPackagePart().getPartName());// 步骤2:反射实例化母版(修复构造器+关系绑定)XSLFSlideMaster customMaster = createValidSlideMaster(ppt, customTheme);System.out.println("母版创建完成:" + customMaster.getPackagePart().getPartName());// 步骤3:创建合法布局(修复XML模板+ID规则)XSLFSlideLayout customLayout = createValidSlideLayout(ppt, customMaster, LAYOUT_TITLE_AND_CONTENT);System.out.println("布局创建完成:" + customLayout.getPackagePart().getPartName());// 步骤4:基于布局创建幻灯片(移除重复绑定)XSLFSlide slide = createValidSlide(ppt, customLayout);System.out.println("幻灯片创建完成:" + slide.getPackagePart().getPartName());// 步骤5:保存PPT(确保流正常关闭)saveValidPPT(ppt, "fixed_low_version_ppt.pptx");System.out.println("PPT生成成功,可正常打开:fixed_low_version_ppt.pptx");} catch (Exception e) {e.printStackTrace();} finally {try {ppt.close();} catch (IOException e) {e.printStackTrace();}}}/*** 修复点1:创建合法Theme(绑定PPT上下文+规范命名)*/private static XSLFTheme createValidTheme(XMLSlideShow ppt) throws Exception {OPCPackage opc = ppt.getPackage();// 规范Theme命名:从theme1.xml开始连续递增int themeIdx = opc.getPartsByContentType(XSLFRelation.THEME.getContentType()).size() + 1;PackagePartName themePartName = PackagingURIHelper.createPartName("/ppt/theme/theme" + themeIdx + ".xml");PackagePart themePart = opc.createPart(themePartName, XSLFRelation.THEME.getContentType());// 写入PowerPoint兼容的Theme XML(完整合法结构)String validThemeXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +"<a:theme xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" name=\"CustomTheme\">" +"<a:themeElements>" +"<a:clrScheme name=\"CustomColor\">" +"<a:dk1><a:srgbClr val=\"000000\"/></a:dk1>" +"<a:lt1><a:srgbClr val=\"FFFFFF\"/></a:lt1>" +"<a:dk2><a:srgbClr val=\"44546A\"/></a:dk2>" +"<a:lt2><a:srgbClr val=\"E7E6E6\"/></a:lt2>" +"<a:accent1><a:srgbClr val=\"4F81BD\"/></a:accent1>" +"<a:accent2><a:srgbClr val=\"C0504D\"/></a:accent2>" +"<a:accent3><a:srgbClr val=\"9BBB59\"/></a:accent3>" +"<a:accent4><a:srgbClr val=\"8064A2\"/></a:accent4>" +"<a:accent5><a:srgbClr val=\"4BACC6\"/></a:accent5>" +"<a:accent6><a:srgbClr val=\"F79646\"/></a:accent6>" +"<a:hlink><a:srgbClr val=\"0000FF\"/></a:hlink>" +"<a:folHlink><a:srgbClr val=\"800080\"/></a:folHlink>" +"</a:clrScheme>" +"<a:fontScheme name=\"CustomFont\">" +"<a:majorFont><a:latin typeface=\"微软雅黑\"/><a:ea typeface=\"微软雅黑\"/><a:cs typeface=\"微软雅黑\"/></a:majorFont>" +"<a:minorFont><a:latin typeface=\"微软雅黑\"/><a:ea typeface=\"微软雅黑\"/><a:cs typeface=\"微软雅黑\"/></a:minorFont>" +"</a:fontScheme>" +"<a:fmtScheme name=\"CustomFormat\">" +"<a:fillStyleLst>" +"<a:solidFill><a:srgbClr val=\"FFFFFF\"/></a:solidFill>" +"<a:gradFill><a:gsLst><a:gs pos=\"0\"><a:srgbClr val=\"E6E6E6\"/></a:gs><a:gs pos=\"100000\"><a:srgbClr val=\"FFFFFF\"/></a:gs></a:gsLst><a:lin ang=\"2700000\" scaled=\"1\"/></a:gradFill>" +"</a:fillStyleLst>" +"<a:lnStyleLst><a:ln w=\"10000\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:srgbClr val=\"B7B7B7\"/></a:solidFill><a:prstDash val=\"solid\"/></a:ln></a:lnStyleLst>" +"<a:effectStyleLst><a:effectStyle><a:efctLst><a:outerShdw blur=\"80000\" dist=\"30000\" dir=\"2700000\" algn=\"ctr\" rotWithShape=\"0\"><a:srgbClr val=\"000000\" alpha=\"30000\"/></a:outerShdw></a:efctLst></a:effectStyle></a:effectStyleLst>" +"<a:bgFillStyleLst><a:solidFill><a:srgbClr val=\"FFFFFF\"/></a:solidFill></a:bgFillStyleLst>" +"</a:fmtScheme>" +"</a:themeElements>" +"<a:objectDefaults/>" +"<a:extraClrSchemeLst/>" +"</a:theme>";try (OutputStream out = themePart.getOutputStream()) {
//            IOUtils.write(validThemeXml.getBytes("UTF-8"), out);IOUtils.copy(new ByteArrayInputStream(validThemeXml.getBytes(StandardCharsets.UTF_8)), out);}// 修复:低版本XSLFTheme构造器需传入PPT上下文return new XSLFTheme(themePart);}/*** 修复点2:创建合法母版(修复构造器+规范XML+关系绑定)*/private static XSLFSlideMaster createValidSlideMaster(XMLSlideShow ppt, XSLFTheme theme) throws Exception {OPCPackage opc = ppt.getPackage();// 规范母版命名:从slideMaster1.xml开始int masterIdx = ppt.getSlideMasters().size() + 1;PackagePartName masterPartName = PackagingURIHelper.createPartName("/ppt/slideMasters/slideMaster" + masterIdx + ".xml");PackagePart masterPart = opc.createPart(masterPartName, XSLFRelation.SLIDE_MASTER.getContentType());// 写入完整合法的母版XML(包含布局ID列表+主题引用)String validMasterXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +"<p:sldMaster xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">" +"<p:bg><p:bgPr><a:solidFill><a:srgbClr val=\"F5F5F5\"/></a:solidFill></p:bgPr></p:bg>" +"<p:txStyles>" +"<p:titleStyle><a:bodyPr lIns=\"50000\" tIns=\"50000\" rIns=\"50000\" bIns=\"50000\" anchor=\"ctr\" vert=\"ctr\" wrap=\"square\" rtlCol=\"0\"/>" +"<a:lstStyle><a:defPPr algn=\"ctr\"><a:defRPr sz=\"3200\" b=\"1\" latin=\"微软雅黑\" ea=\"微软雅黑\" cs=\"微软雅黑\"/></a:defPPr></a:lstStyle>" +"</p:titleStyle>" +"<p:bodyStyle><a:bodyPr lIns=\"50000\" tIns=\"20000\" rIns=\"50000\" bIns=\"20000\" anchor=\"t\" vert=\"t\" wrap=\"square\" rtlCol=\"0\"/>" +"<a:lstStyle><a:defPPr algn=\"l\"><a:defRPr sz=\"1800\" latin=\"微软雅黑\" ea=\"微软雅黑\" cs=\"微软雅黑\"/></a:defPPr></a:lstStyle>" +"</p:bodyStyle>" +"</p:txStyles>" +"<p:themeOverride>" +"<a:themeElements>" +"<a:fontScheme name=\"CustomFont\">" +"<a:majorFont><a:latin typeface=\"微软雅黑\"/><a:ea typeface=\"微软雅黑\"/><a:cs typeface=\"微软雅黑\"/></a:majorFont>" +"<a:minorFont><a:latin typeface=\"微软雅黑\"/><a:ea typeface=\"微软雅黑\"/><a:cs typeface=\"微软雅黑\"/></a:minorFont>" +"</a:fontScheme>" +"</a:themeElements>" +"</p:themeOverride>" +"<p:sldLayoutIdLst>" +"<p:sldLayoutId id=\"2147483648\" r:id=\"rId2\"/>" + // 预留布局ID,避免解析失败"</p:sldLayoutIdLst>" +"<p:transition/>" +"<p:timing/>" +"</p:sldMaster>";try (OutputStream out = masterPart.getOutputStream()) {
//            IOUtils.write(validMasterXml.getBytes("UTF-8"), out);IOUtils.copy(new ByteArrayInputStream(validMasterXml.getBytes(StandardCharsets.UTF_8)), out);}// 母版绑定主题(唯一合法关系:母版→主题)String themeRId = "rId1";masterPart.addRelationship(theme.getPackagePart().getPartName(),INTERNAL, XSLFRelation.THEME.getRelation(), themeRId);// 修复:动态适配母版构造器(兼容所有POI 3.17子版本)XSLFSlideMaster master = null;Constructor<?>[] constructors = XSLFSlideMaster.class.getDeclaredConstructors();for (Constructor<?> constructor : constructors) {Class<?>[] paramTypes = constructor.getParameterTypes();if (paramTypes.length == 1 && paramTypes[0] == PackagePart.class) {constructor.setAccessible(true);master = (XSLFSlideMaster) constructor.newInstance(masterPart);break;} else if (paramTypes.length == 2) {constructor.setAccessible(true);try {if (paramTypes[0] == XMLSlideShow.class && paramTypes[1] == PackagePart.class) {master = (XSLFSlideMaster) constructor.newInstance(ppt, masterPart);} else if (paramTypes[0] == PackagePart.class && paramTypes[1] == XMLSlideShow.class) {master = (XSLFSlideMaster) constructor.newInstance(masterPart, ppt);}if (master != null) break;} catch (Exception e) {continue;}}}if (master == null) {throw new RuntimeException("未找到可用的XSLFSlideMaster构造器");}// 加入母版列表+初始化_layouts Mapppt.getSlideMasters().add(master);initMasterLayoutsMap(master);return master;}/*** 修复点3:创建合法布局(移除无效流读取+规范XML+ID规则)*/private static XSLFSlideLayout createValidSlideLayout(XMLSlideShow ppt, XSLFSlideMaster master, int layoutType) throws Exception {OPCPackage opc = ppt.getPackage();// 修复:Layout命名从1开始连续递增,移除硬编码indexint layoutIdx = layoutSeq++;PackagePartName layoutPartName = PackagingURIHelper.createPartName("/ppt/slideLayouts/slideLayout" + layoutIdx + ".xml");PackagePart layoutPart = opc.createPart(layoutPartName, XSLFRelation.SLIDE_LAYOUT.getContentType());// 写入PowerPoint兼容的标题+内容布局XML(替换无效流读取)String validLayoutXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +"<p:sldLayout xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" type=\"titleAndContent\">" +"<p:cSld>" +"<p:spTree>" +"<p:nvGrpSpPr><p:cNvPr id=\"1\" name=\"标题和内容布局\"/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>" +"<p:grpSpPr><a:xfrm><a:off x=\"0\" y=\"0\"/><a:ext cx=\"9144000\" cy=\"6858000\"/><a:chOff x=\"0\" y=\"0\"/><a:chExt cx=\"9144000\" cy=\"6858000\"/></a:xfrm></p:grpSpPr>" +// 标题占位符"<p:sp>" +"<p:nvSpPr><p:cNvPr id=\"2\" name=\"标题\"/><p:cNvSpPr><a:spLocks noGrp=\"1\"/><p:ph type=\"title\" idx=\"1\"/></p:cNvSpPr><p:nvPr/></p:nvSpPr>" +"<p:spPr><a:xfrm><a:off x=\"500000\" y=\"500000\"/><a:ext cx=\"8144000\" cy=\"800000\"/></a:xfrm><a:prstGeom prst=\"rect\"><a:avLst/></a:prstGeom></p:spPr>" +"<p:txBody><a:bodyPr lIns=\"50000\" tIns=\"20000\" rIns=\"50000\" bIns=\"20000\" anchor=\"t\" vert=\"t\" wrap=\"square\" rtlCol=\"0\"/>" +"<a:lstStyle><a:defPPr algn=\"ctr\"><a:defRPr sz=\"3200\" b=\"1\" latin=\"微软雅黑\"/></a:defPPr></a:lstStyle>" +"<a:p><a:r><a:t>标题占位符</a:t></a:r></a:p>" +"</p:txBody>" +"</p:sp>" +// 内容占位符"<p:sp>" +"<p:nvSpPr><p:cNvPr id=\"3\" name=\"内容\"/><p:cNvSpPr><a:spLocks noGrp=\"1\"/><p:ph type=\"body\" idx=\"2\"/></p:cNvSpPr><p:nvPr/></p:nvSpPr>" +"<p:spPr><a:xfrm><a:off x=\"500000\" y=\"1500000\"/><a:ext cx=\"8144000\" cy=\"4500000\"/></a:xfrm><a:prstGeom prst=\"rect\"><a:avLst/></a:prstGeom></p:spPr>" +"<p:txBody><a:bodyPr lIns=\"50000\" tIns=\"20000\" rIns=\"50000\" bIns=\"20000\" anchor=\"t\" vert=\"t\" wrap=\"square\" rtlCol=\"0\"/>" +"<a:lstStyle><a:defPPr algn=\"l\"><a:defRPr sz=\"1800\" latin=\"微软雅黑\"/></a:defPPr></a:lstStyle>" +"<a:p><a:r><a:t>内容占位符</a:t></a:r></a:p>" +"</p:txBody>" +"</p:sp>" +"</p:spTree>" +"<p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>" +"</p:cSld>" +"<p:transition/>" +"</p:sldLayout>";try (OutputStream out = layoutPart.getOutputStream()) {
//            IOUtils.write(validLayoutXml.getBytes("UTF-8"), out);IOUtils.copy(new ByteArrayInputStream(validLayoutXml.getBytes(StandardCharsets.UTF_8)), out);}// 布局绑定母版(唯一合法关系:布局→母版)String masterRId = "rId1";layoutPart.addRelationship(master.getPackagePart().getPartName(),INTERNAL, XSLFRelation.SLIDE_MASTER.getRelation(), masterRId);// 动态适配布局构造器XSLFSlideLayout layout = null;Constructor<?>[] constructors = XSLFSlideLayout.class.getDeclaredConstructors();for (Constructor<?> constructor : constructors) {Class<?>[] paramTypes = constructor.getParameterTypes();if (paramTypes.length == 1 && paramTypes[0] == PackagePart.class) {constructor.setAccessible(true);layout = (XSLFSlideLayout) constructor.newInstance(layoutPart);break;} else if (paramTypes.length == 2 && paramTypes[0] == XSLFSlideMaster.class && paramTypes[1] == PackagePart.class) {constructor.setAccessible(true);layout = (XSLFSlideLayout) constructor.newInstance(master, layoutPart);break;}}if (layout == null) {throw new RuntimeException("未找到可用的XSLFSlideLayout构造器");}// 修复:兼容反射字段名(master/_master)bindLayoutMaster(layout, master);// 加入母版_layouts MapMap<String, XSLFSlideLayout> layoutsMap = getMasterLayoutsMap(master);layoutsMap.put("slideLayout" + layoutIdx, layout);return layout;}/*** 修复点4:创建合法幻灯片(移除重复关系绑定)*/private static XSLFSlide createValidSlide(XMLSlideShow ppt, XSLFSlideLayout layout) throws Exception {// 修复:低版本POI需先创建空白幻灯片,再绑定布局(避免自动绑定失败)XSLFSlide slide = ppt.createSlide();PackagePart slidePart = slide.getPackagePart();// 幻灯片绑定布局(仅需一次,避免重复)if (!slidePart.getRelationshipsByType(XSLFRelation.SLIDE_LAYOUT.getRelation()).iterator().hasNext()) {slidePart.addRelationship(layout.getPackagePart().getPartName(),INTERNAL, XSLFRelation.SLIDE_LAYOUT.getRelation(), "rId1");}// 添加测试内容(使用占位符而非手动创建文本框,兼容布局)XSLFTextShape titleShape = (XSLFTextShape)slide.getPlaceholder(Placeholder.TITLE);
//        XSLFSimpleShape titleShape = slide.getPlaceholder(Placeholder.TITLE);if (titleShape != null) {titleShape.setText("低版本POI修复版演示");}XSLFTextShape contentShape = (XSLFTextShape)slide.getPlaceholder(Placeholder.BODY);if (contentShape != null) {contentShape.setText("1. 修复XML结构不合法问题\n2. 清理重复关系绑定\n3. 规范文件命名规则\n4. 兼容PowerPoint解析");}return slide;}// ===================== 工具方法(修复+兼容) =====================private static void initMasterLayoutsMap(XSLFSlideMaster master) throws Exception {Field layoutsField = getField(XSLFSlideMaster.class, "_layouts", "layouts");layoutsField.setAccessible(true);if (layoutsField.get(master) == null) {layoutsField.set(master, new HashMap<String, XSLFSlideLayout>());}}private static Map<String, XSLFSlideLayout> getMasterLayoutsMap(XSLFSlideMaster master) throws Exception {Field layoutsField = getField(XSLFSlideMaster.class, "_layouts", "layouts");layoutsField.setAccessible(true);return (Map<String, XSLFSlideLayout>) layoutsField.get(master);}private static void bindLayoutMaster(XSLFSlideLayout layout, XSLFSlideMaster master) throws Exception {Field masterField = getField(XSLFSlideLayout.class, "_master", "master");masterField.setAccessible(true);masterField.set(layout, master);}/*** 兼容反射字段名(适配不同POI子版本的_master/master字段)*/private static Field getField(Class<?> clazz, String... fieldNames) throws NoSuchFieldException {for (String fieldName : fieldNames) {try {return clazz.getDeclaredField(fieldName);} catch (NoSuchFieldException e) {continue;}}throw new NoSuchFieldException("未找到字段:" + String.join(",", fieldNames));}private static void saveValidPPT(XMLSlideShow ppt, String path) throws IOException {// 修复:确保所有流刷新并正常写入try (FileOutputStream fos = new FileOutputStream(path)) {ppt.write(fos);fos.flush();}}
}

  

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询