001/* 002 * Copyright (c) 2023-2026, Agents-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.agentsflex.core.file2text.extractor.impl; 017 018import com.agentsflex.core.file2text.extractor.FileExtractor; 019import com.agentsflex.core.file2text.source.DocumentSource; 020import org.apache.poi.xslf.usermodel.*; 021import org.apache.xmlbeans.XmlException; 022 023import java.io.IOException; 024import java.io.InputStream; 025import java.util.*; 026 027/** 028 * PPTX 文档提取器(.pptx) 029 * 提取幻灯片中的标题、段落、表格文本 030 */ 031public class PptxExtractor implements FileExtractor { 032 033 private static final Set<String> SUPPORTED_MIME_TYPES; 034 private static final String MIME_PREFIX = "application/vnd.openxmlformats-officedocument.presentationml"; 035 private static final Set<String> SUPPORTED_EXTENSIONS; 036 037 static { 038 // 精确 MIME(可选) 039 Set<String> mimeTypes = new HashSet<>(); 040 mimeTypes.add("application/vnd.openxmlformats-officedocument.presentationml.presentation"); 041 mimeTypes.add("application/vnd.openxmlformats-officedocument.presentationml.slideshow"); 042 SUPPORTED_MIME_TYPES = Collections.unmodifiableSet(mimeTypes); 043 044 // 支持的扩展名 045 Set<String> extensions = new HashSet<>(); 046 extensions.add("pptx"); 047 extensions.add("ppsx"); 048 extensions.add("potx"); 049 SUPPORTED_EXTENSIONS = Collections.unmodifiableSet(extensions); 050 } 051 052 @Override 053 public boolean supports(DocumentSource source) { 054 String mimeType = source.getMimeType(); 055 String fileName = source.getFileName(); 056 057 // 1. MIME 精确匹配 058 if (mimeType != null && SUPPORTED_MIME_TYPES.contains(mimeType)) { 059 return true; 060 } 061 062 // 2. MIME 前缀匹配 063 if (mimeType != null && mimeType.startsWith(MIME_PREFIX)) { 064 return true; 065 } 066 067 // 3. 扩展名匹配 068 if (fileName != null) { 069 String ext = getExtension(fileName); 070 if (ext != null && SUPPORTED_EXTENSIONS.contains(ext.toLowerCase())) { 071 return true; 072 } 073 } 074 075 return false; 076 } 077 078 @Override 079 public String extractText(DocumentSource source) throws IOException { 080 StringBuilder text = new StringBuilder(); 081 082 try (InputStream is = source.openStream(); 083 XMLSlideShow slideShow = new XMLSlideShow(is)) { 084 085 List<XSLFSlide> slides = slideShow.getSlides(); 086 087 for (int i = 0; i < slides.size(); i++) { 088 XSLFSlide slide = slides.get(i); 089 text.append("\n--- Slide ").append(i + 1).append(" ---\n"); 090 091 // 提取所有形状中的文本 092 for (XSLFShape shape : slide.getShapes()) { 093 if (shape instanceof XSLFTextShape) { 094 XSLFTextShape textShape = (XSLFTextShape) shape; 095 String shapeText = textShape.getText(); 096 if (shapeText != null && !shapeText.trim().isEmpty()) { 097 text.append(shapeText).append("\n"); 098 } 099 } 100 } 101 102 // 可选:提取表格 103 extractTablesFromSlide(slide, text); 104 } 105 106 } catch (XmlException e) { 107 throw new IOException("Invalid PPTX structure: " + e.getMessage(), e); 108 } catch (Exception e) { 109 throw new IOException("Failed to extract PPTX: " + e.getMessage(), e); 110 } 111 112 return text.toString().trim(); 113 } 114 115 /** 116 * 提取幻灯片中的表格内容 117 */ 118 private void extractTablesFromSlide(XSLFSlide slide, StringBuilder text) { 119 for (XSLFShape shape : slide.getShapes()) { 120 if (shape instanceof XSLFTable) { 121 XSLFTable table = (XSLFTable) shape; 122 text.append("\n[Table Start]\n"); 123 for (XSLFTableRow row : table.getRows()) { 124 List<String> cellTexts = new ArrayList<>(); 125 for (XSLFTableCell cell : row.getCells()) { 126 String cellText = cell.getText(); 127 cellTexts.add(cellText != null ? cellText.trim() : ""); 128 } 129 text.append(String.join(" | ", cellTexts)).append("\n"); 130 } 131 text.append("[Table End]\n"); 132 } 133 } 134 } 135 136 @Override 137 public int getOrder() { 138 return 10; 139 } 140 141 private String getExtension(String fileName) { 142 if (fileName == null || !fileName.contains(".")) return null; 143 int lastDot = fileName.lastIndexOf('.'); 144 return fileName.substring(lastDot + 1); 145 } 146}