2017-09-21 5 views
0

Apache POI 3.16을 사용하여 PowerPoint 슬라이드에서 차트를 제거하려고하는데 어려움이 있습니다.Apache POI를 사용하여 PowerPoint 슬라이드에서 차트 제거

  1. 열기 기존의 파워 포인트 문서 (템플릿 문서)
  2. 추가 및 제거 슬라이드 기존 슬라이드

이 작품

  • 업데이트 차트 :

    우리의 코드는 다음 단계를 수행합니다 벌금.

    어떤 시점에서 특정 슬라이드에서 차트를 제거해야합니다.

    OPCPackage pkg = ppt.getPackage(); 
    
    String chartRelationId = slide.getRelationId(chart); 
    pkg.removeRelationship(chartRelationId); 
    
    pkg.removePart(chart.getPackagePart()); 
    

    pkg.removePart() 호출이 작동하지만, 디스크에 최종 파워 포인트 문서를 작성하는 것은 예외가 부품 파일 (우리가 이미 삭제 아마도 때문에) 제거 할 수 없습니다 말하는 실패 할 것 : 여기에 우리의 시도입니다.

    pkg.removeRelationship() 호출은 또한 core.xml이라는 디스크에 문서를 쓰는 동안 예외를 트리거합니다.

    Apache POI를 사용하여 PowerPoint 슬라이드에서 차트를 제거 할 수 있습니까? 그렇다면 어떻게?

  • 답변

    2

    XSLFChart은 @ 베타 상태이므로 지금까지 차트에 대해 명시적인 Shape이 없습니다. 따라서 apache poi을 사용하면 차트가 포함 된 XSLFGraphicFrame만을 얻을 수 있습니다. 그러나 슬라이드에서 XSLFGraphicFrame을 제거해도 관련된 모든 차트 부분이 제거되지는 않습니다. 따라서 관련 차트 부분을 위에서 아래로 제거하는 것은 POIXMLDocumentPart 레벨에서 PackagePart 레벨까지 내려 가기 때문에 지금까지 구현되지 않았습니다. 그리고 POIXMLDocumentPart의 모든 관련 메소드가 보호되고 XSLFChart 자체가 최종적인 것이므로 실제로 해결하기가 쉽지 않습니다.

    다음 코드는 문제를 보여줍니다. 그것은 그러한 것으로 주석 처리됩니다.

    코드는 첫 번째 슬라이드에서 모든 차트를 제거하고 /ppt/embeddings/Microsoft_Excel_WorksheetN.xlsx, /ppt/charts/colorsN.xml/ppt/charts/styleN.xml과 같은 모든 관계 및 관련 부분을 제거합니다. /ppt/charts/chartN.xml 만 주석 처리되었으므로 제거 할 수 없습니다. 그들에게 더 이상 관계가 없기 때문에

    import java.io.FileInputStream; 
    import java.io.FileOutputStream; 
    
    import org.apache.poi.xslf.usermodel.*; 
    import org.apache.poi.sl.usermodel.*; 
    
    import org.apache.poi.POIXMLDocumentPart; 
    
    import org.apache.poi.openxml4j.opc.OPCPackage; 
    import org.apache.poi.openxml4j.opc.PackagePart; 
    import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; 
    import org.apache.poi.openxml4j.opc.PackageRelationship; 
    
    import org.apache.xmlbeans.XmlObject; 
    
    import java.util.Map; 
    import java.util.HashMap; 
    
    import java.util.regex.Pattern; 
    
    public class ReadPPTRemoveChart { 
    
    public static void main(String[] args) throws Exception { 
    
        XMLSlideShow slideShow = new XMLSlideShow(new FileInputStream("PPTWithCharts.pptx")); 
    
        XSLFSlide slide = slideShow.getSlides().get(0); 
    
        Map<String, XSLFGraphicFrame> chartFramesToRemove = new HashMap<>(); 
    
        for (XSLFShape shape : slide.getShapes()) { 
        if (shape instanceof XSLFGraphicFrame) { 
        XSLFGraphicFrame graphicframe = (XSLFGraphicFrame)shape; 
        XmlObject xmlobject = graphicframe.getXmlObject(); 
        XmlObject[] graphics = xmlobject.selectPath(
              "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + 
              ".//a:graphic"); 
        if (graphics.length > 0) { //we have a XSLFGraphicFrame containing a:graphic 
        XmlObject graphic = graphics[0]; 
        XmlObject[] charts = graphic.selectPath(
              "declare namespace c='http://schemas.openxmlformats.org/drawingml/2006/chart' " + 
              ".//c:chart"); 
        if (charts.length > 0) { //we have a XSLFGraphicFrame containing c:chart 
         XmlObject chart = charts[0]; 
         String rid = chart.selectAttribute(
              "http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id") 
              .newCursor().getTextValue(); 
         chartFramesToRemove.put(rid, graphicframe); 
        } 
        } 
        } 
        } 
    
        PackagePart slidepart = slide.getPackagePart(); 
        OPCPackage opcpackage = slideShow.getPackage(); 
    
        for (String rid : chartFramesToRemove.keySet()) { 
        //at frist remove the XSLFGraphicFrame 
        XSLFGraphicFrame chartFrame = chartFramesToRemove.get(rid); 
        slide.removeShape(chartFrame); 
        //Here is the problem in my opinion. This **should** remove all related parts too. 
        //But since XSLFChart is @Beta, it does not. 
    
        //So we try doing removing the related parts manually. 
        //we get the PackagePart of the chart 
        PackageRelationship relship = slidepart.getRelationships().getRelationshipByID(rid); 
        PackagePart chartpart = slidepart.getRelatedPart(relship); 
    
        //now we get and remove all the relations and related PackageParts from this chartpart 
        //this are /ppt/embeddings/Microsoft_Excel_WorksheetN.xlsx, /ppt/charts/colorsN.xml 
        //and /ppt/charts/styleN.xml 
        for (PackageRelationship chartrelship : chartpart.getRelationships()) { 
        String partname = chartrelship.getTargetURI().toString(); 
        PackagePart part = opcpackage.getPartsByName(Pattern.compile(partname)).get(0); 
        opcpackage.removePart(part); 
        chartpart.removeRelationship(chartrelship.getId()); 
        } 
        //this works 
    
        //now we **should** be able removing the relationship to the chartpart from the slide too 
        //but this seems not to be possible 
        //doing this on PackagePart level works: 
        slidepart.removeRelationship(rid); 
        for (PackageRelationship sliderelship : slidepart.getRelationships()) { 
        System.out.println("rel PP level: " + sliderelship.getTargetURI().toString()); 
        } 
        //all relationships to /ppt/charts/chartN.xml are removed 
    
        //but on POIXMLDocumentPart level this has no effect 
        for (POIXMLDocumentPart sliderelpart : slide.getRelations()) { 
        System.out.println("rel POIXML level: " + sliderelpart.getPackagePart().getPartName()); 
        } 
        //relationships to /ppt/charts/chartN.xml are **not** removed 
    
        //So we cannot remove the chartpart. 
        //If we would do this, then while slideShow.write the 
        //org.apache.poi.xslf.usermodel.XSLFChart.commit in XSLFChart.java fails 
        //because after removing the PackagePart is absent but the relation is still there. 
        //opcpackage.removePart(chartpart); 
    
        } 
    
    
        slideShow.write(new FileOutputStream("PPTWithChartsNew.pptx")); 
        slideShow.close(); 
    } 
    } 
    

    PowerPoint를 사용하여 PPTWithChartsNew.pptx를 열고 다음을 저장 한 후, 불필요한 /ppt/charts/styleN.xml 부분도 제거된다.


    편집 2017년 9월 24일 :

    반사를 사용하여 솔루션을 발견. 마찬가지로, 관련 차트 부분을 제거하려면 POIXMLDocumentPart 레벨에서 PackagePart 레벨까지의 하향식을 사용해야합니다. POIXMLDocumentPart.removeRelation이 보호되어 있으므로 리플렉션을 사용하여이를 수행해야합니다.

    import java.io.FileInputStream; 
    import java.io.FileOutputStream; 
    
    import org.apache.poi.xslf.usermodel.*; 
    import org.apache.poi.sl.usermodel.*; 
    
    import org.apache.poi.POIXMLDocumentPart; 
    
    import org.apache.poi.openxml4j.opc.OPCPackage; 
    import org.apache.poi.openxml4j.opc.PackagePart; 
    import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; 
    import org.apache.poi.openxml4j.opc.PackageRelationship; 
    
    import org.apache.xmlbeans.XmlObject; 
    
    import java.util.Map; 
    import java.util.HashMap; 
    
    import java.util.regex.Pattern; 
    
    import java.lang.reflect.Method; 
    
    public class ReadPPTRemoveChart { 
    
    public static void main(String[] args) throws Exception { 
    
        XMLSlideShow slideShow = new XMLSlideShow(new FileInputStream("PPTWithCharts.pptx")); 
    
        XSLFSlide slide = slideShow.getSlides().get(0); 
    
        Map<String, XSLFGraphicFrame> chartFramesToRemove = new HashMap<>(); 
    
        for (XSLFShape shape : slide.getShapes()) { 
        if (shape instanceof XSLFGraphicFrame) { 
        XSLFGraphicFrame graphicframe = (XSLFGraphicFrame)shape; 
        XmlObject xmlobject = graphicframe.getXmlObject(); 
        XmlObject[] graphics = xmlobject.selectPath(
              "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + 
              ".//a:graphic"); 
        if (graphics.length > 0) { //we have a XSLFGraphicFrame containing a:graphic 
        XmlObject graphic = graphics[0]; 
        XmlObject[] charts = graphic.selectPath(
              "declare namespace c='http://schemas.openxmlformats.org/drawingml/2006/chart' " + 
              ".//c:chart"); 
        if (charts.length > 0) { //we have a XSLFGraphicFrame containing c:chart 
         XmlObject chart = charts[0]; 
         String rid = chart.selectAttribute(
              "http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id") 
              .newCursor().getTextValue(); 
         chartFramesToRemove.put(rid, graphicframe); 
        } 
        } 
        } 
        } 
    
        PackagePart slidepart = slide.getPackagePart(); 
        OPCPackage opcpackage = slideShow.getPackage(); 
    
        for (String rid : chartFramesToRemove.keySet()) { 
        //at frist remove the XSLFGraphicFrame 
        XSLFGraphicFrame chartFrame = chartFramesToRemove.get(rid); 
        slide.removeShape(chartFrame); 
        //Here is the problem in my opinion. This **should** remove all related parts too. 
        //But since XSLFChart is @Beta, it does not. 
    
        //So we try doing removing the related parts manually. 
    
        //we get the PackagePart of the chart 
        PackageRelationship relship = slidepart.getRelationships().getRelationshipByID(rid); 
        PackagePart chartpart = slidepart.getRelatedPart(relship); 
    
        //now we get and remove all the relations and related PackageParts from this chartpart 
        //this are /ppt/embeddings/Microsoft_Excel_WorksheetN.xlsx, /ppt/charts/colorsN.xml 
        //and /ppt/charts/styleN.xml 
        for (PackageRelationship chartrelship : chartpart.getRelationships()) { 
        String partname = chartrelship.getTargetURI().toString(); 
        PackagePart part = opcpackage.getPartsByName(Pattern.compile(partname)).get(0); 
        opcpackage.removePart(part); 
        chartpart.removeRelationship(chartrelship.getId()); 
        } 
    
        //now we remove the chart part from the slide part 
        //We need doing this on POIXMLDocumentPart level. 
        //Since POIXMLDocumentPart.removeRelation is protected, we need doing this using reflection 
        XSLFChart chart = (XSLFChart)slide.getRelationById(rid); 
        Method removeRelation = POIXMLDocumentPart.class.getDeclaredMethod("removeRelation", POIXMLDocumentPart.class); 
        removeRelation.setAccessible(true); 
        removeRelation.invoke(slide, chart); 
    
        } 
    
        slideShow.write(new FileOutputStream("PPTWithChartsNew.pptx")); 
        slideShow.close(); 
    } 
    } 
    
    +0

    감사합니다. –