From: Andreas Beeker Date: Sat, 16 Oct 2021 00:00:50 +0000 (+0000) Subject: #64036 - Replace reflection calls in factories for Java 9+ X-Git-Tag: REL_5_2_0~368 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=dfbab3ff6af87898de6101ea827db7e75cecca61;p=poi.git #64036 - Replace reflection calls in factories for Java 9+ use ServiceLoader for HSLF Metro Shapes git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1894298 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFMetroShape.java b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFMetroShape.java index 69931a2e91..5912d05745 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFMetroShape.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFMetroShape.java @@ -28,7 +28,7 @@ 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.sl.usermodel.Shape; +import org.apache.poi.sl.usermodel.MetroShapeProvider; import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; @@ -39,18 +39,21 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; * This is the helper class for HSLFMetroShape to dive into OOXML classes */ @Internal -public class XSLFMetroShape { - /* - * parses the metro bytes to a XSLF shape - */ - public static Shape parseShape(byte[] metroBytes) - throws InvalidFormatException, IOException, XmlException { - PackagePartName shapePN = PackagingURIHelper.createPartName("/drs/shapexml.xml"); +public class XSLFMetroShape implements MetroShapeProvider { + /** parses the metro bytes to a XSLF shape */ + @Override + public XSLFShape parseShape(byte[] metroBytes) throws IOException { try (OPCPackage pkg = OPCPackage.open(new UnsynchronizedByteArrayInputStream(metroBytes))) { + PackagePartName shapePN = PackagingURIHelper.createPartName("/drs/shapexml.xml"); PackagePart shapePart = pkg.getPart(shapePN); + if (shapePart == null) { + return null; + } CTGroupShape gs = CTGroupShape.Factory.parse(shapePart.getInputStream(), DEFAULT_XML_OPTIONS); XSLFGroupShape xgs = new XSLFGroupShape(gs, null); return xgs.getShapes().get(0); + } catch (InvalidFormatException | XmlException e) { + throw new IOException("can't parse metro shape", e); } } } diff --git a/poi-ooxml/src/main/java9/module-info.class b/poi-ooxml/src/main/java9/module-info.class index f4db29c614..d79f2de643 100644 Binary files a/poi-ooxml/src/main/java9/module-info.class and b/poi-ooxml/src/main/java9/module-info.class differ diff --git a/poi-ooxml/src/main/java9/module-info.java b/poi-ooxml/src/main/java9/module-info.java index b11e2a9ebe..51533acbd6 100644 --- a/poi-ooxml/src/main/java9/module-info.java +++ b/poi-ooxml/src/main/java9/module-info.java @@ -34,6 +34,7 @@ module org.apache.poi.ooxml { provides org.apache.poi.ss.usermodel.WorkbookProvider with org.apache.poi.xssf.usermodel.XSSFWorkbookFactory; provides org.apache.poi.sl.usermodel.SlideShowProvider with org.apache.poi.xslf.usermodel.XSLFSlideShowFactory; provides org.apache.poi.sl.draw.ImageRenderer with org.apache.poi.xslf.draw.SVGImageRenderer; + provides org.apache.poi.sl.usermodel.MetroShapeProvider with org.apache.poi.xslf.usermodel.XSLFMetroShape; exports org.apache.poi.xwpf.extractor; exports org.apache.poi.xwpf.usermodel; diff --git a/poi-ooxml/src/main/resources/META-INF/services/org.apache.poi.sl.usermodel.MetroShapeProvider b/poi-ooxml/src/main/resources/META-INF/services/org.apache.poi.sl.usermodel.MetroShapeProvider new file mode 100644 index 0000000000..ba99414720 --- /dev/null +++ b/poi-ooxml/src/main/resources/META-INF/services/org.apache.poi.sl.usermodel.MetroShapeProvider @@ -0,0 +1,18 @@ +# ==================================================================== +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ==================================================================== + +org.apache.poi.xslf.usermodel.XSLFMetroShape \ No newline at end of file diff --git a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java index 372e0216f4..397db98d12 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java @@ -17,7 +17,10 @@ package org.apache.poi.xslf.usermodel; import static org.apache.poi.sl.usermodel.BaseTestSlideShow.getColor; +import static org.apache.poi.sl.usermodel.TextRun.FieldType.DATE_TIME; +import static org.apache.poi.sl.usermodel.TextRun.FieldType.SLIDE_NUMBER; import static org.apache.poi.xslf.usermodel.TestXSLFSimpleShape.getSpPr; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -27,17 +30,23 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeFalse; import java.awt.Color; -import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import org.apache.poi.POIDataSamples; +import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.sl.usermodel.Placeholder; import org.apache.poi.sl.usermodel.Shape; import org.apache.poi.sl.usermodel.SlideShow; import org.apache.poi.sl.usermodel.SlideShowFactory; +import org.apache.poi.sl.usermodel.TextParagraph; import org.apache.poi.sl.usermodel.TextParagraph.TextAlign; +import org.apache.poi.sl.usermodel.TextRun; +import org.apache.poi.sl.usermodel.TextRun.FieldType; +import org.apache.poi.sl.usermodel.TextShape; import org.apache.poi.sl.usermodel.VerticalAlignment; import org.apache.poi.xddf.usermodel.text.XDDFBodyProperties; import org.apache.poi.xddf.usermodel.text.XDDFTextBody; @@ -66,19 +75,19 @@ class TestXSLFTextShape { @Test void testLayouts() throws IOException { - XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("layouts.pptx"); + try (XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("layouts.pptx")) { - List slide = ppt.getSlides(); + List slide = ppt.getSlides(); - verifySlide1(slide.get(0)); - verifySlide2(slide.get(1)); - verifySlide3(slide.get(2)); - verifySlide4(slide.get(3)); - verifySlide7(slide.get(6)); - verifySlide8(slide.get(7)); - verifySlide10(slide.get(9)); + verifySlide1(slide.get(0)); + verifySlide2(slide.get(1)); + verifySlide3(slide.get(2)); + verifySlide4(slide.get(3)); + verifySlide7(slide.get(6)); + verifySlide8(slide.get(7)); + verifySlide10(slide.get(9)); - ppt.close(); + } } void verifySlide1(XSLFSlide slide){ @@ -90,7 +99,7 @@ class TestXSLFTextShape { XDDFTextBody tb1 = shape1.getTextBody(); XDDFBodyProperties tbp1 = tb1.getBodyProperties(); CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); - assertEquals(STPlaceholderType.CTR_TITLE, ph1.getType()); + assertSame(STPlaceholderType.CTR_TITLE, ph1.getType()); // anchor is not defined in the shape assertNull(getSpPr(shape1).getXfrm()); @@ -129,7 +138,7 @@ class TestXSLFTextShape { XDDFTextBody tb2 = shape2.getTextBody(); XDDFBodyProperties tbp2 = tb2.getBodyProperties(); CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false); - assertEquals(STPlaceholderType.SUB_TITLE, ph2.getType()); + assertSame(STPlaceholderType.SUB_TITLE, ph2.getType()); // anchor is not defined in the shape assertNull(getSpPr(shape2).getXfrm()); @@ -171,7 +180,7 @@ class TestXSLFTextShape { XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); - assertEquals(STPlaceholderType.TITLE, ph1.getType()); + assertSame(STPlaceholderType.TITLE, ph1.getType()); // anchor is not defined in the shape assertNull(getSpPr(shape1).getXfrm()); @@ -284,7 +293,7 @@ class TestXSLFTextShape { XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); - assertEquals(STPlaceholderType.TITLE, ph1.getType()); + assertSame(STPlaceholderType.TITLE, ph1.getType()); // anchor is not defined in the shape assertNull(getSpPr(shape1).getXfrm()); @@ -318,7 +327,7 @@ class TestXSLFTextShape { XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1); CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false); - assertEquals(STPlaceholderType.BODY, ph2.getType()); + assertSame(STPlaceholderType.BODY, ph2.getType()); // anchor is not defined in the shape assertNull(getSpPr(shape2).getXfrm()); @@ -355,7 +364,7 @@ class TestXSLFTextShape { XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); - assertEquals(STPlaceholderType.TITLE, ph1.getType()); + assertSame(STPlaceholderType.TITLE, ph1.getType()); // anchor is not defined in the shape assertNull(getSpPr(shape1).getXfrm()); @@ -470,7 +479,7 @@ class TestXSLFTextShape { XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); - assertEquals(STPlaceholderType.TITLE, ph1.getType()); + assertSame(STPlaceholderType.TITLE, ph1.getType()); // anchor is not defined in the shape assertNull(getSpPr(shape1).getXfrm()); @@ -538,7 +547,7 @@ class TestXSLFTextShape { XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); - assertEquals(STPlaceholderType.TITLE, ph1.getType()); + assertSame(STPlaceholderType.TITLE, ph1.getType()); // anchor is not defined in the shape assertNull(getSpPr(shape1).getXfrm()); @@ -664,301 +673,313 @@ class TestXSLFTextShape { @Test void testTitleStyles() throws IOException { - XMLSlideShow ppt = new XMLSlideShow(); - - XSLFSlideMaster master = ppt.getSlideMasters().get(0); - XSLFTheme theme = master.getTheme(); - XSLFSlideLayout layout = master.getLayout(SlideLayout.TITLE); - XSLFSlide slide = ppt.createSlide(layout) ; - assertSame(layout, slide.getSlideLayout()); - assertSame(master, slide.getSlideMaster()); - - XSLFTextShape titleShape = slide.getPlaceholder(0); - titleShape.setText("Apache POI"); - XSLFTextParagraph paragraph = titleShape.getTextParagraphs().get(0); - XSLFTextRun textRun = paragraph.getTextRuns().get(0); - - // level 1 : default title style on the master slide - // /p:sldMaster/p:txStyles/p:titleStyle/a:lvl1pPr - CTTextParagraphProperties lv1PPr = master.getXmlObject().getTxStyles().getTitleStyle().getLvl1PPr(); - CTTextCharacterProperties lv1CPr = lv1PPr.getDefRPr(); - assertEquals(4400, lv1CPr.getSz()); - assertEquals(44.0, textRun.getFontSize(), 0); - assertEquals("+mj-lt", lv1CPr.getLatin().getTypeface()); - assertEquals("Calibri", theme.getMajorFont()); - assertEquals("Calibri", textRun.getFontFamily()); - lv1CPr.setSz(3200); - assertEquals(32.0, textRun.getFontSize(), 0); - lv1CPr.getLatin().setTypeface("Arial"); - assertEquals("Arial", textRun.getFontFamily()); - assertEquals(STTextAlignType.CTR, lv1PPr.getAlgn()); - assertEquals(TextAlign.CENTER, paragraph.getTextAlign()); - lv1PPr.setAlgn(STTextAlignType.L); - assertEquals(TextAlign.LEFT, paragraph.getTextAlign()); - - // level 2: title placeholder on the master slide - // /p:sldMaster/p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="title"] - XSLFTextShape tx2 = master.getPlaceholder(0); - CTTextParagraphProperties lv2PPr = tx2.getTextBody(true).getLstStyle().addNewLvl1PPr(); - CTTextCharacterProperties lv2CPr = lv2PPr.addNewDefRPr(); - lv2CPr.setSz(3300); - assertEquals(33.0, textRun.getFontSize(), 0); - lv2CPr.addNewLatin().setTypeface("Times"); - assertEquals("Times", textRun.getFontFamily()); - lv2PPr.setAlgn(STTextAlignType.R); - assertEquals(TextAlign.RIGHT, paragraph.getTextAlign()); - - - // level 3: title placeholder on the slide layout - // /p:sldLayout /p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="ctrTitle"] - XSLFTextShape tx3 = layout.getPlaceholder(0); - CTTextParagraphProperties lv3PPr = tx3.getTextBody(true).getLstStyle().addNewLvl1PPr(); - CTTextCharacterProperties lv3CPr = lv3PPr.addNewDefRPr(); - lv3CPr.setSz(3400); - assertEquals(34.0, textRun.getFontSize(), 0); - lv3CPr.addNewLatin().setTypeface("Courier New"); - assertEquals("Courier New", textRun.getFontFamily()); - lv3PPr.setAlgn(STTextAlignType.CTR); - assertEquals(TextAlign.CENTER, paragraph.getTextAlign()); - - // level 4: default text properties in the shape itself - // ./p:sp/p:txBody/a:lstStyle/a:lvl1pPr - CTTextParagraphProperties lv4PPr = titleShape.getTextBody(true).getLstStyle().addNewLvl1PPr(); - CTTextCharacterProperties lv4CPr = lv4PPr.addNewDefRPr(); - lv4CPr.setSz(3500); - assertEquals(35.0, textRun.getFontSize(), 0); - lv4CPr.addNewLatin().setTypeface("Arial"); - assertEquals("Arial", textRun.getFontFamily()); - lv4PPr.setAlgn(STTextAlignType.L); - assertEquals(TextAlign.LEFT, paragraph.getTextAlign()); - - // level 5: text properties are defined in the text run - CTTextParagraphProperties lv5PPr = paragraph.getXmlObject().addNewPPr(); - CTTextCharacterProperties lv5CPr = textRun.getRPr(false); - lv5CPr.setSz(3600); - assertEquals(36.0, textRun.getFontSize(), 0); - lv5CPr.addNewLatin().setTypeface("Calibri"); - assertEquals("Calibri", textRun.getFontFamily()); - lv5PPr.setAlgn(STTextAlignType.CTR); - assertEquals(TextAlign.CENTER, paragraph.getTextAlign()); - - ppt.close(); + try (XMLSlideShow ppt = new XMLSlideShow()) { + + XSLFSlideMaster master = ppt.getSlideMasters().get(0); + XSLFTheme theme = master.getTheme(); + XSLFSlideLayout layout = master.getLayout(SlideLayout.TITLE); + XSLFSlide slide = ppt.createSlide(layout); + assertSame(layout, slide.getSlideLayout()); + assertSame(master, slide.getSlideMaster()); + + XSLFTextShape titleShape = slide.getPlaceholder(0); + titleShape.setText("Apache POI"); + XSLFTextParagraph paragraph = titleShape.getTextParagraphs().get(0); + XSLFTextRun textRun = paragraph.getTextRuns().get(0); + + // level 1 : default title style on the master slide + // /p:sldMaster/p:txStyles/p:titleStyle/a:lvl1pPr + CTTextParagraphProperties lv1PPr = master.getXmlObject().getTxStyles().getTitleStyle().getLvl1PPr(); + CTTextCharacterProperties lv1CPr = lv1PPr.getDefRPr(); + assertEquals(4400, lv1CPr.getSz()); + assertEquals(44.0, textRun.getFontSize(), 0); + assertEquals("+mj-lt", lv1CPr.getLatin().getTypeface()); + assertEquals("Calibri", theme.getMajorFont()); + assertEquals("Calibri", textRun.getFontFamily()); + lv1CPr.setSz(3200); + assertEquals(32.0, textRun.getFontSize(), 0); + lv1CPr.getLatin().setTypeface("Arial"); + assertEquals("Arial", textRun.getFontFamily()); + assertSame(STTextAlignType.CTR, lv1PPr.getAlgn()); + assertEquals(TextAlign.CENTER, paragraph.getTextAlign()); + lv1PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, paragraph.getTextAlign()); + + // level 2: title placeholder on the master slide + // /p:sldMaster/p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="title"] + XSLFTextShape tx2 = master.getPlaceholder(0); + CTTextParagraphProperties lv2PPr = tx2.getTextBody(true).getLstStyle().addNewLvl1PPr(); + CTTextCharacterProperties lv2CPr = lv2PPr.addNewDefRPr(); + lv2CPr.setSz(3300); + assertEquals(33.0, textRun.getFontSize(), 0); + lv2CPr.addNewLatin().setTypeface("Times"); + assertEquals("Times", textRun.getFontFamily()); + lv2PPr.setAlgn(STTextAlignType.R); + assertEquals(TextAlign.RIGHT, paragraph.getTextAlign()); + + + // level 3: title placeholder on the slide layout + // /p:sldLayout /p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="ctrTitle"] + XSLFTextShape tx3 = layout.getPlaceholder(0); + CTTextParagraphProperties lv3PPr = tx3.getTextBody(true).getLstStyle().addNewLvl1PPr(); + CTTextCharacterProperties lv3CPr = lv3PPr.addNewDefRPr(); + lv3CPr.setSz(3400); + assertEquals(34.0, textRun.getFontSize(), 0); + lv3CPr.addNewLatin().setTypeface("Courier New"); + assertEquals("Courier New", textRun.getFontFamily()); + lv3PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, paragraph.getTextAlign()); + + // level 4: default text properties in the shape itself + // ./p:sp/p:txBody/a:lstStyle/a:lvl1pPr + CTTextParagraphProperties lv4PPr = titleShape.getTextBody(true).getLstStyle().addNewLvl1PPr(); + CTTextCharacterProperties lv4CPr = lv4PPr.addNewDefRPr(); + lv4CPr.setSz(3500); + assertEquals(35.0, textRun.getFontSize(), 0); + lv4CPr.addNewLatin().setTypeface("Arial"); + assertEquals("Arial", textRun.getFontFamily()); + lv4PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, paragraph.getTextAlign()); + + // level 5: text properties are defined in the text run + CTTextParagraphProperties lv5PPr = paragraph.getXmlObject().addNewPPr(); + CTTextCharacterProperties lv5CPr = textRun.getRPr(false); + lv5CPr.setSz(3600); + assertEquals(36.0, textRun.getFontSize(), 0); + lv5CPr.addNewLatin().setTypeface("Calibri"); + assertEquals("Calibri", textRun.getFontFamily()); + lv5PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, paragraph.getTextAlign()); + } } @Test void testBodyStyles() throws IOException { - XMLSlideShow ppt = new XMLSlideShow(); - - XSLFSlideMaster master = ppt.getSlideMasters().get(0); - XSLFTheme theme = master.getTheme(); - XSLFSlideLayout layout = master.getLayout(SlideLayout.TITLE_AND_CONTENT); - XSLFSlide slide = ppt.createSlide(layout) ; - assertSame(layout, slide.getSlideLayout()); - assertSame(master, slide.getSlideMaster()); - - XSLFTextShape tx1 = slide.getPlaceholder(1); - tx1.clearText(); - - XSLFTextParagraph p1 = tx1.addNewTextParagraph(); - assertEquals(0, p1.getIndentLevel()); - XSLFTextRun r1 = p1.addNewTextRun(); - r1.setText("Apache POI"); - - XSLFTextParagraph p2 = tx1.addNewTextParagraph(); - p2.setIndentLevel(1); - assertEquals(1, p2.getIndentLevel()); - XSLFTextRun r2 = p2.addNewTextRun(); - r2.setText("HSLF"); - - XSLFTextParagraph p3 = tx1.addNewTextParagraph(); - p3.setIndentLevel(2); - assertEquals(2, p3.getIndentLevel()); - XSLFTextRun r3 = p3.addNewTextRun(); - r3.setText("XSLF"); - - // level 1 : default title style on the master slide - // /p:sldMaster/p:txStyles/p:bodyStyle/a:lvl1pPr - CTTextParagraphProperties lv1PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl1PPr(); - CTTextCharacterProperties lv1CPr = lv1PPr.getDefRPr(); - CTTextParagraphProperties lv2PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl2PPr(); - CTTextCharacterProperties lv2CPr = lv2PPr.getDefRPr(); - CTTextParagraphProperties lv3PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl3PPr(); - CTTextCharacterProperties lv3CPr = lv3PPr.getDefRPr(); - // lv1 - assertEquals(3200, lv1CPr.getSz()); - assertEquals(32.0, r1.getFontSize(), 0); - assertEquals("+mn-lt", lv1CPr.getLatin().getTypeface()); - assertEquals("Calibri", theme.getMinorFont()); - assertEquals("Calibri", r1.getFontFamily()); - lv1CPr.setSz(3300); - assertEquals(33.0, r1.getFontSize(), 0); - lv1CPr.getLatin().setTypeface("Arial"); - assertEquals("Arial", r1.getFontFamily()); - assertEquals(STTextAlignType.L, lv1PPr.getAlgn()); - assertEquals(TextAlign.LEFT, p1.getTextAlign()); - lv1PPr.setAlgn(STTextAlignType.R); - assertEquals(TextAlign.RIGHT, p1.getTextAlign()); - //lv2 - assertEquals(2800, lv2CPr.getSz()); - assertEquals(28.0, r2.getFontSize(), 0); - lv2CPr.setSz(3300); - assertEquals(33.0, r2.getFontSize(), 0); - lv2CPr.getLatin().setTypeface("Times"); - assertEquals("Times", r2.getFontFamily()); - assertEquals(STTextAlignType.L, lv2PPr.getAlgn()); - assertEquals(TextAlign.LEFT, p2.getTextAlign()); - lv2PPr.setAlgn(STTextAlignType.R); - assertEquals(TextAlign.RIGHT, p2.getTextAlign()); - //lv3 - assertEquals(2400, lv3CPr.getSz()); - assertEquals(24.0, r3.getFontSize(), 0); - lv3CPr.setSz(2500); - assertEquals(25.0, r3.getFontSize(), 0); - lv3CPr.getLatin().setTypeface("Courier New"); - assertEquals("Courier New", r3.getFontFamily()); - assertEquals(STTextAlignType.L, lv3PPr.getAlgn()); - assertEquals(TextAlign.LEFT, p3.getTextAlign()); - lv3PPr.setAlgn(STTextAlignType.R); - assertEquals(TextAlign.RIGHT, p3.getTextAlign()); - - - // level 2: body placeholder on the master slide - // /p:sldMaster/p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="body"] - XSLFTextShape tx2 = master.getPlaceholder(1); - assertEquals(Placeholder.BODY, tx2.getTextType()); - - lv1PPr = tx2.getTextBody(true).getLstStyle().addNewLvl1PPr(); - lv1CPr = lv1PPr.addNewDefRPr(); - lv2PPr = tx2.getTextBody(true).getLstStyle().addNewLvl2PPr(); - lv2CPr = lv2PPr.addNewDefRPr(); - lv3PPr = tx2.getTextBody(true).getLstStyle().addNewLvl3PPr(); - lv3CPr = lv3PPr.addNewDefRPr(); - - lv1CPr.setSz(3300); - assertEquals(33.0, r1.getFontSize(), 0); - lv1CPr.addNewLatin().setTypeface("Times"); - assertEquals("Times", r1.getFontFamily()); - lv1PPr.setAlgn(STTextAlignType.L); - assertEquals(TextAlign.LEFT, p1.getTextAlign()); - - lv2CPr.setSz(3300); - assertEquals(33.0, r2.getFontSize(), 0); - lv2CPr.addNewLatin().setTypeface("Times"); - assertEquals("Times", r2.getFontFamily()); - lv2PPr.setAlgn(STTextAlignType.L); - assertEquals(TextAlign.LEFT, p2.getTextAlign()); - - lv3CPr.setSz(3300); - assertEquals(33.0, r3.getFontSize(), 0); - lv3CPr.addNewLatin().setTypeface("Times"); - assertEquals("Times", r3.getFontFamily()); - lv3PPr.setAlgn(STTextAlignType.L); - assertEquals(TextAlign.LEFT, p3.getTextAlign()); - - // level 3: body placeholder on the slide layout - // /p:sldLayout /p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="ctrTitle"] - XSLFTextShape tx3 = layout.getPlaceholder(1); - assertEquals(Placeholder.BODY, tx2.getTextType()); - lv1PPr = tx3.getTextBody(true).getLstStyle().addNewLvl1PPr(); - lv1CPr = lv1PPr.addNewDefRPr(); - lv2PPr = tx3.getTextBody(true).getLstStyle().addNewLvl2PPr(); - lv2CPr = lv2PPr.addNewDefRPr(); - lv3PPr = tx3.getTextBody(true).getLstStyle().addNewLvl3PPr(); - lv3CPr = lv3PPr.addNewDefRPr(); - - lv1CPr.setSz(3400); - assertEquals(34.0, r1.getFontSize(), 0); - lv1CPr.addNewLatin().setTypeface("Courier New"); - assertEquals("Courier New", r1.getFontFamily()); - lv1PPr.setAlgn(STTextAlignType.CTR); - assertEquals(TextAlign.CENTER, p1.getTextAlign()); - - lv2CPr.setSz(3400); - assertEquals(34.0, r2.getFontSize(), 0); - lv2CPr.addNewLatin().setTypeface("Courier New"); - assertEquals("Courier New", r2.getFontFamily()); - lv2PPr.setAlgn(STTextAlignType.CTR); - assertEquals(TextAlign.CENTER, p2.getTextAlign()); - - lv3CPr.setSz(3400); - assertEquals(34.0, r3.getFontSize(), 0); - lv3CPr.addNewLatin().setTypeface("Courier New"); - assertEquals("Courier New", r3.getFontFamily()); - lv3PPr.setAlgn(STTextAlignType.CTR); - assertEquals(TextAlign.CENTER, p3.getTextAlign()); - - // level 4: default text properties in the shape itself - // ./p:sp/p:txBody/a:lstStyle/a:lvl1pPr - lv1PPr = tx1.getTextBody(true).getLstStyle().addNewLvl1PPr(); - lv1CPr = lv1PPr.addNewDefRPr(); - lv2PPr = tx1.getTextBody(true).getLstStyle().addNewLvl2PPr(); - lv2CPr = lv2PPr.addNewDefRPr(); - lv3PPr = tx1.getTextBody(true).getLstStyle().addNewLvl3PPr(); - lv3CPr = lv3PPr.addNewDefRPr(); - - lv1CPr.setSz(3500); - assertEquals(35.0, r1.getFontSize(), 0); - lv1CPr.addNewLatin().setTypeface("Arial"); - assertEquals("Arial", r1.getFontFamily()); - lv1PPr.setAlgn(STTextAlignType.L); - assertEquals(TextAlign.LEFT, p1.getTextAlign()); - - lv2CPr.setSz(3500); - assertEquals(35.0, r2.getFontSize(), 0); - lv2CPr.addNewLatin().setTypeface("Arial"); - assertEquals("Arial", r2.getFontFamily()); - lv2PPr.setAlgn(STTextAlignType.L); - assertEquals(TextAlign.LEFT, p2.getTextAlign()); - - lv3CPr.setSz(3500); - assertEquals(35.0, r3.getFontSize(), 0); - lv3CPr.addNewLatin().setTypeface("Arial"); - assertEquals("Arial", r3.getFontFamily()); - lv3PPr.setAlgn(STTextAlignType.L); - assertEquals(TextAlign.LEFT, p3.getTextAlign()); - - // level 5: text properties are defined in the text run - lv1PPr = p1.getXmlObject().isSetPPr() ? p1.getXmlObject().getPPr() : p1.getXmlObject().addNewPPr(); - lv1CPr = r1.getRPr(false); - lv2PPr = p2.getXmlObject().isSetPPr() ? p2.getXmlObject().getPPr() : p2.getXmlObject().addNewPPr(); - lv2CPr = r2.getRPr(false); - lv3PPr = p3.getXmlObject().isSetPPr() ? p3.getXmlObject().getPPr() : p3.getXmlObject().addNewPPr(); - lv3CPr = r3.getRPr(false); - - lv1CPr.setSz(3600); - assertEquals(36.0, r1.getFontSize(), 0); - lv1CPr.addNewLatin().setTypeface("Calibri"); - assertEquals("Calibri", r1.getFontFamily()); - lv1PPr.setAlgn(STTextAlignType.CTR); - assertEquals(TextAlign.CENTER, p1.getTextAlign()); - - lv2CPr.setSz(3600); - assertEquals(36.0, r2.getFontSize(), 0); - lv2CPr.addNewLatin().setTypeface("Calibri"); - assertEquals("Calibri", r2.getFontFamily()); - lv2PPr.setAlgn(STTextAlignType.CTR); - assertEquals(TextAlign.CENTER, p2.getTextAlign()); - - lv3CPr.setSz(3600); - assertEquals(36.0, r3.getFontSize(), 0); - lv3CPr.addNewLatin().setTypeface("Calibri"); - assertEquals("Calibri", r3.getFontFamily()); - lv3PPr.setAlgn(STTextAlignType.CTR); - assertEquals(TextAlign.CENTER, p3.getTextAlign()); - - ppt.close(); + try (XMLSlideShow ppt = new XMLSlideShow()) { + + XSLFSlideMaster master = ppt.getSlideMasters().get(0); + XSLFTheme theme = master.getTheme(); + XSLFSlideLayout layout = master.getLayout(SlideLayout.TITLE_AND_CONTENT); + XSLFSlide slide = ppt.createSlide(layout); + assertSame(layout, slide.getSlideLayout()); + assertSame(master, slide.getSlideMaster()); + + XSLFTextShape tx1 = slide.getPlaceholder(1); + tx1.clearText(); + + XSLFTextParagraph p1 = tx1.addNewTextParagraph(); + assertEquals(0, p1.getIndentLevel()); + XSLFTextRun r1 = p1.addNewTextRun(); + r1.setText("Apache POI"); + + XSLFTextParagraph p2 = tx1.addNewTextParagraph(); + p2.setIndentLevel(1); + assertEquals(1, p2.getIndentLevel()); + XSLFTextRun r2 = p2.addNewTextRun(); + r2.setText("HSLF"); + + XSLFTextParagraph p3 = tx1.addNewTextParagraph(); + p3.setIndentLevel(2); + assertEquals(2, p3.getIndentLevel()); + XSLFTextRun r3 = p3.addNewTextRun(); + r3.setText("XSLF"); + + // level 1 : default title style on the master slide + // /p:sldMaster/p:txStyles/p:bodyStyle/a:lvl1pPr + CTTextParagraphProperties lv1PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl1PPr(); + CTTextCharacterProperties lv1CPr = lv1PPr.getDefRPr(); + CTTextParagraphProperties lv2PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl2PPr(); + CTTextCharacterProperties lv2CPr = lv2PPr.getDefRPr(); + CTTextParagraphProperties lv3PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl3PPr(); + CTTextCharacterProperties lv3CPr = lv3PPr.getDefRPr(); + // lv1 + assertEquals(3200, lv1CPr.getSz()); + assertEquals(32.0, r1.getFontSize(), 0); + assertEquals("+mn-lt", lv1CPr.getLatin().getTypeface()); + assertEquals("Calibri", theme.getMinorFont()); + assertEquals("Calibri", r1.getFontFamily()); + lv1CPr.setSz(3300); + assertEquals(33.0, r1.getFontSize(), 0); + lv1CPr.getLatin().setTypeface("Arial"); + assertEquals("Arial", r1.getFontFamily()); + assertSame(STTextAlignType.L, lv1PPr.getAlgn()); + assertEquals(TextAlign.LEFT, p1.getTextAlign()); + lv1PPr.setAlgn(STTextAlignType.R); + assertEquals(TextAlign.RIGHT, p1.getTextAlign()); + //lv2 + assertEquals(2800, lv2CPr.getSz()); + assertEquals(28.0, r2.getFontSize(), 0); + lv2CPr.setSz(3300); + assertEquals(33.0, r2.getFontSize(), 0); + lv2CPr.getLatin().setTypeface("Times"); + assertEquals("Times", r2.getFontFamily()); + assertSame(STTextAlignType.L, lv2PPr.getAlgn()); + assertEquals(TextAlign.LEFT, p2.getTextAlign()); + lv2PPr.setAlgn(STTextAlignType.R); + assertEquals(TextAlign.RIGHT, p2.getTextAlign()); + //lv3 + assertEquals(2400, lv3CPr.getSz()); + assertEquals(24.0, r3.getFontSize(), 0); + lv3CPr.setSz(2500); + assertEquals(25.0, r3.getFontSize(), 0); + lv3CPr.getLatin().setTypeface("Courier New"); + assertEquals("Courier New", r3.getFontFamily()); + assertSame(STTextAlignType.L, lv3PPr.getAlgn()); + assertEquals(TextAlign.LEFT, p3.getTextAlign()); + lv3PPr.setAlgn(STTextAlignType.R); + assertEquals(TextAlign.RIGHT, p3.getTextAlign()); + + + // level 2: body placeholder on the master slide + // /p:sldMaster/p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="body"] + XSLFTextShape tx2 = master.getPlaceholder(1); + assertEquals(Placeholder.BODY, tx2.getTextType()); + + lv1PPr = tx2.getTextBody(true).getLstStyle().addNewLvl1PPr(); + lv1CPr = lv1PPr.addNewDefRPr(); + lv2PPr = tx2.getTextBody(true).getLstStyle().addNewLvl2PPr(); + lv2CPr = lv2PPr.addNewDefRPr(); + lv3PPr = tx2.getTextBody(true).getLstStyle().addNewLvl3PPr(); + lv3CPr = lv3PPr.addNewDefRPr(); + + lv1CPr.setSz(3300); + assertEquals(33.0, r1.getFontSize(), 0); + lv1CPr.addNewLatin().setTypeface("Times"); + assertEquals("Times", r1.getFontFamily()); + lv1PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p1.getTextAlign()); + + lv2CPr.setSz(3300); + assertEquals(33.0, r2.getFontSize(), 0); + lv2CPr.addNewLatin().setTypeface("Times"); + assertEquals("Times", r2.getFontFamily()); + lv2PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p2.getTextAlign()); + + lv3CPr.setSz(3300); + assertEquals(33.0, r3.getFontSize(), 0); + lv3CPr.addNewLatin().setTypeface("Times"); + assertEquals("Times", r3.getFontFamily()); + lv3PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p3.getTextAlign()); + + // level 3: body placeholder on the slide layout + // /p:sldLayout /p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="ctrTitle"] + XSLFTextShape tx3 = layout.getPlaceholder(1); + assertEquals(Placeholder.BODY, tx2.getTextType()); + lv1PPr = tx3.getTextBody(true).getLstStyle().addNewLvl1PPr(); + lv1CPr = lv1PPr.addNewDefRPr(); + lv2PPr = tx3.getTextBody(true).getLstStyle().addNewLvl2PPr(); + lv2CPr = lv2PPr.addNewDefRPr(); + lv3PPr = tx3.getTextBody(true).getLstStyle().addNewLvl3PPr(); + lv3CPr = lv3PPr.addNewDefRPr(); + + lv1CPr.setSz(3400); + assertEquals(34.0, r1.getFontSize(), 0); + lv1CPr.addNewLatin().setTypeface("Courier New"); + assertEquals("Courier New", r1.getFontFamily()); + lv1PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p1.getTextAlign()); + + lv2CPr.setSz(3400); + assertEquals(34.0, r2.getFontSize(), 0); + lv2CPr.addNewLatin().setTypeface("Courier New"); + assertEquals("Courier New", r2.getFontFamily()); + lv2PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p2.getTextAlign()); + + lv3CPr.setSz(3400); + assertEquals(34.0, r3.getFontSize(), 0); + lv3CPr.addNewLatin().setTypeface("Courier New"); + assertEquals("Courier New", r3.getFontFamily()); + lv3PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p3.getTextAlign()); + + // level 4: default text properties in the shape itself + // ./p:sp/p:txBody/a:lstStyle/a:lvl1pPr + lv1PPr = tx1.getTextBody(true).getLstStyle().addNewLvl1PPr(); + lv1CPr = lv1PPr.addNewDefRPr(); + lv2PPr = tx1.getTextBody(true).getLstStyle().addNewLvl2PPr(); + lv2CPr = lv2PPr.addNewDefRPr(); + lv3PPr = tx1.getTextBody(true).getLstStyle().addNewLvl3PPr(); + lv3CPr = lv3PPr.addNewDefRPr(); + + lv1CPr.setSz(3500); + assertEquals(35.0, r1.getFontSize(), 0); + lv1CPr.addNewLatin().setTypeface("Arial"); + assertEquals("Arial", r1.getFontFamily()); + lv1PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p1.getTextAlign()); + + lv2CPr.setSz(3500); + assertEquals(35.0, r2.getFontSize(), 0); + lv2CPr.addNewLatin().setTypeface("Arial"); + assertEquals("Arial", r2.getFontFamily()); + lv2PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p2.getTextAlign()); + + lv3CPr.setSz(3500); + assertEquals(35.0, r3.getFontSize(), 0); + lv3CPr.addNewLatin().setTypeface("Arial"); + assertEquals("Arial", r3.getFontFamily()); + lv3PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p3.getTextAlign()); + + // level 5: text properties are defined in the text run + lv1PPr = p1.getXmlObject().isSetPPr() ? p1.getXmlObject().getPPr() : p1.getXmlObject().addNewPPr(); + lv1CPr = r1.getRPr(false); + lv2PPr = p2.getXmlObject().isSetPPr() ? p2.getXmlObject().getPPr() : p2.getXmlObject().addNewPPr(); + lv2CPr = r2.getRPr(false); + lv3PPr = p3.getXmlObject().isSetPPr() ? p3.getXmlObject().getPPr() : p3.getXmlObject().addNewPPr(); + lv3CPr = r3.getRPr(false); + + lv1CPr.setSz(3600); + assertEquals(36.0, r1.getFontSize(), 0); + lv1CPr.addNewLatin().setTypeface("Calibri"); + assertEquals("Calibri", r1.getFontFamily()); + lv1PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p1.getTextAlign()); + + lv2CPr.setSz(3600); + assertEquals(36.0, r2.getFontSize(), 0); + lv2CPr.addNewLatin().setTypeface("Calibri"); + assertEquals("Calibri", r2.getFontFamily()); + lv2PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p2.getTextAlign()); + + lv3CPr.setSz(3600); + assertEquals(36.0, r3.getFontSize(), 0); + lv3CPr.addNewLatin().setTypeface("Calibri"); + assertEquals("Calibri", r3.getFontFamily()); + lv3PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p3.getTextAlign()); + } } @Test void metroBlob() throws IOException, ReflectiveOperationException { assumeFalse(xslfOnly); - File f = POIDataSamples.getSlideShowInstance().getFile("bug52297.ppt"); - SlideShow ppt = SlideShowFactory.create(f); - Shape sh = ppt.getSlides().get(1).getShapes().get(3); - XSLFAutoShape xsh = (XSLFAutoShape)sh.getClass().getMethod("getMetroShape").invoke(sh); - String textExp = " ___ ___ ___ ________ __ _______ ___ ___________ __________ __ _____ ___ ___ ___ _______ ____ ______ ___________ _____________ ___ _______ ______ ____ ______ __ ___________ __________ ___ _________ _____ ________ __________ ___ _______ __________ "; - String textAct = xsh.getText(); - ppt.close(); - assertEquals(textExp, textAct); + POIDataSamples samples = POIDataSamples.getSlideShowInstance(); + String textAct; + try (InputStream is = samples.openResourceAsStream("bug52297.ppt"); + SlideShow ppt = SlideShowFactory.create(is)) { + // check metro shapes on master sheet + MasterSheet master = ppt.getSlideMasters().get(0); + FieldType[] tl = master.getShapes().stream(). + filter(f -> f instanceof TextShape).map(f -> (TextShape)f). + flatMap(f -> ((List>) f.getTextParagraphs()).stream()). + flatMap(f -> f.getTextRuns().stream()). + map(TextRun::getFieldType). + filter(Objects::nonNull). + toArray(FieldType[]::new); + assertArrayEquals(new FieldType[]{DATE_TIME, SLIDE_NUMBER, SLIDE_NUMBER}, tl); + + // check metro shapes on slides + Shape sh = ppt.getSlides().get(1).getShapes().get(3); + XSLFAutoShape xsh = (XSLFAutoShape) sh.getClass().getMethod("getMetroShape").invoke(sh); + String textExp = " ___ ___ ___ ________ __ _______ ___ ___________ __________ __ _____ ___ ___ ___ _______ ____ ______ ___________ _____________ ___ _______ ______ ____ ______ __ ___________ __________ ___ _________ _____ ________ __________ ___ _______ __________ "; + textAct = xsh.getText(); + assertEquals(textExp, textAct); + } } } \ No newline at end of file diff --git a/poi-ooxml/src/test/java9/module-info.class b/poi-ooxml/src/test/java9/module-info.class index 2cd55eb942..75444a4c92 100644 Binary files a/poi-ooxml/src/test/java9/module-info.class and b/poi-ooxml/src/test/java9/module-info.class differ diff --git a/poi-ooxml/src/test/java9/module-info.java b/poi-ooxml/src/test/java9/module-info.java index 3bb546f87f..a6a78d6e4e 100644 --- a/poi-ooxml/src/test/java9/module-info.java +++ b/poi-ooxml/src/test/java9/module-info.java @@ -32,6 +32,7 @@ module org.apache.poi.ooxml { provides org.apache.poi.ss.usermodel.WorkbookProvider with org.apache.poi.xssf.usermodel.XSSFWorkbookFactory; provides org.apache.poi.sl.usermodel.SlideShowProvider with org.apache.poi.xslf.usermodel.XSLFSlideShowFactory; provides org.apache.poi.sl.draw.ImageRenderer with org.apache.poi.xslf.draw.SVGImageRenderer; + provides org.apache.poi.sl.usermodel.MetroShapeProvider with org.apache.poi.xslf.usermodel.XSLFMetroShape; exports org.apache.poi.xwpf.extractor; exports org.apache.poi.xwpf.usermodel; diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/model/HSLFMetroShape.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/model/HSLFMetroShape.java index 54495aab2a..cc64c4c82f 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/model/HSLFMetroShape.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/model/HSLFMetroShape.java @@ -17,7 +17,8 @@ package org.apache.poi.hslf.model; -import java.lang.reflect.Method; +import java.io.IOException; +import java.util.ServiceLoader; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -26,7 +27,10 @@ import org.apache.poi.ddf.EscherComplexProperty; import org.apache.poi.ddf.EscherPropertyTypes; import org.apache.poi.ddf.EscherTertiaryOptRecord; import org.apache.poi.hslf.usermodel.HSLFShape; +import org.apache.poi.sl.usermodel.MetroShapeProvider; import org.apache.poi.sl.usermodel.Shape; +import org.apache.poi.sl.usermodel.TextParagraph; +import org.apache.poi.sl.usermodel.TextRun; import org.apache.poi.util.Internal; /** @@ -34,7 +38,10 @@ import org.apache.poi.util.Internal; * containing an ooxml representation of the shape */ @Internal -public class HSLFMetroShape> { +public class HSLFMetroShape< + S extends Shape, + P extends TextParagraph +> { private static final Logger LOGGER = LogManager.getLogger(HSLFMetroShape.class); private final HSLFShape shape; @@ -42,9 +49,9 @@ public class HSLFMetroShape> { public HSLFMetroShape(HSLFShape shape) { this.shape = shape; } - + /** - * @return the bytes of the metro blob, which are bytes of an OPCPackage, i.e. a zip stream + * @return the bytes of the metro blob, which are bytes of an OPCPackage, i.e. a zip stream */ public byte[] getMetroBytes() { EscherComplexProperty ep = getMetroProp(); @@ -57,33 +64,36 @@ public class HSLFMetroShape> { public boolean hasMetroBlob() { return getMetroProp() != null; } - + private EscherComplexProperty getMetroProp() { AbstractEscherOptRecord opt = shape.getEscherChild(EscherTertiaryOptRecord.RECORD_ID); return (opt == null) ? null : (EscherComplexProperty)opt.lookup(EscherPropertyTypes.GROUPSHAPE__METROBLOB.propNumber); } - + /** * @return the metro blob shape or null if either there's no metro blob or the ooxml classes * aren't in the classpath */ @SuppressWarnings("unchecked") - public T getShape() { + public Shape getShape() { byte[] metroBytes = getMetroBytes(); if (metroBytes == null) { return null; } - - // org.apache.poi.xslf.usermodel.XSLFMetroShape - ClassLoader cl = getClass().getClassLoader(); - try { - Class ms = cl.loadClass("org.apache.poi.xslf.usermodel.XSLFMetroShape"); - Method m = ms.getMethod("parseShape", byte[].class); - return (T)m.invoke(null, new Object[]{metroBytes}); - } catch (Exception e) { - LOGGER.atError().withThrowable(e).log("can't process metro blob, check if all dependencies for POI OOXML are in the classpath."); - return null; + + ClassLoader cl = HSLFMetroShape.class.getClassLoader(); + IOException lastError = null; + for (MetroShapeProvider msp : ServiceLoader.load(MetroShapeProvider.class, cl)) { + try { + return (Shape) msp.parseShape(metroBytes); + } catch (IOException ex) { + lastError = ex; + break; + } } + + LOGGER.atError().withThrowable(lastError).log("can't process metro blob, check if all dependencies for POI OOXML are in the classpath."); + return null; } } diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextRun.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextRun.java index f69ee6e432..a30281eeeb 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextRun.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextRun.java @@ -19,6 +19,7 @@ package org.apache.poi.hslf.usermodel; import java.awt.Color; import java.util.List; +import java.util.stream.Stream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -35,6 +36,7 @@ import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; import org.apache.poi.sl.usermodel.Placeholder; +import org.apache.poi.sl.usermodel.Shape; import org.apache.poi.sl.usermodel.TextParagraph; import org.apache.poi.sl.usermodel.TextRun; import org.apache.poi.sl.usermodel.TextShape; @@ -506,16 +508,14 @@ public final class HSLFTextRun implements TextRun { } } - if (ts.getSheet() instanceof MasterSheet) { - TextShape> ms = ts.getMetroShape(); - if (ms == null || ms.getTextParagraphs().isEmpty()) { - return null; - } - List trList = ms.getTextParagraphs().get(0).getTextRuns(); - if (trList.isEmpty()) { - return null; - } - return trList.get(0).getFieldType(); + Shape ms = (ts.getSheet() instanceof MasterSheet) ? ts.getMetroShape() : null; + if (ms instanceof TextShape) { + return Stream.of((TextShape)ms). + flatMap(tsh -> ((List>)tsh.getTextParagraphs()).stream()). + flatMap(tph -> tph.getTextRuns().stream()). + findFirst(). + map(TextRun::getFieldType). + orElse(null); } return null; diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextShape.java b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextShape.java index 9a60a2e8ae..8efa7cadb1 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextShape.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hslf/usermodel/HSLFTextShape.java @@ -51,6 +51,7 @@ import org.apache.poi.sl.draw.DrawFactory; import org.apache.poi.sl.draw.DrawTextShape; import org.apache.poi.sl.usermodel.Insets2D; import org.apache.poi.sl.usermodel.Placeholder; +import org.apache.poi.sl.usermodel.Shape; import org.apache.poi.sl.usermodel.ShapeContainer; import org.apache.poi.sl.usermodel.TextParagraph; import org.apache.poi.sl.usermodel.TextRun; @@ -153,7 +154,7 @@ implements TextShape { /** * Create a TextBox object and initialize it from the supplied Record container. * - * @param escherRecord EscherSpContainer container which holds information about this shape + * @param escherRecord {@code EscherSpContainer} container which holds information about this shape * @param parent the parent of the shape */ protected HSLFTextShape(EscherContainerRecord escherRecord, ShapeContainer parent){ @@ -192,7 +193,7 @@ implements TextShape { /** * When a textbox is added to a sheet we need to tell upper-level - * PPDrawing about it. + * {@code PPDrawing} about it. * * @param sh the sheet we are adding to */ @@ -403,7 +404,7 @@ implements TextShape { /** * Sets the type of alignment for the text. - * One of the Anchor* constants defined in this class. + * One of the {@code Anchor*} constants defined in this class. * * @param isCentered horizontal centered? * @param vAlign vertical alignment @@ -572,7 +573,7 @@ implements TextShape { * Returns the value indicating word wrap. * * @return the value indicating word wrap. - * Must be one of the Wrap* constants defined in this class. + * Must be one of the {@code Wrap*} constants defined in this class. * * @see MSOWRAPMODE */ @@ -586,7 +587,7 @@ implements TextShape { * Specifies how the text should be wrapped * * @param wrap the value indicating how the text should be wrapped. - * Must be one of the Wrap* constants defined in this class. + * Must be one of the {@code Wrap*} constants defined in this class. */ public void setWordWrapEx(int wrap){ setEscherProperty(EscherPropertyTypes.TEXT__WRAPTEXT, wrap); @@ -841,7 +842,7 @@ implements TextShape { /** * Returns the array of all hyperlinks in this text run * - * @return the array of all hyperlinks in this text run or null + * @return the array of all hyperlinks in this text run or {@code null} * if not found. */ public List getHyperlinks() { @@ -904,8 +905,10 @@ implements TextShape { * * @return null, if there's no alternative representation, otherwise the text shape */ - public TextShape> getMetroShape() { - HSLFMetroShape>> mbs = new HSLFMetroShape<>(this); - return mbs.getShape(); + public < + S extends Shape, + P extends TextParagraph + > Shape getMetroShape() { + return new HSLFMetroShape(this).getShape(); } } \ No newline at end of file diff --git a/poi-scratchpad/src/main/java9/module-info.class b/poi-scratchpad/src/main/java9/module-info.class index 72189ac318..1c076d2f61 100644 Binary files a/poi-scratchpad/src/main/java9/module-info.class and b/poi-scratchpad/src/main/java9/module-info.class differ diff --git a/poi-scratchpad/src/main/java9/module-info.java b/poi-scratchpad/src/main/java9/module-info.java index f2b2732ec5..ea505f6c1c 100644 --- a/poi-scratchpad/src/main/java9/module-info.java +++ b/poi-scratchpad/src/main/java9/module-info.java @@ -23,6 +23,8 @@ module org.apache.poi.scratchpad { requires org.apache.commons.codec; requires org.apache.logging.log4j; + uses org.apache.poi.sl.usermodel.MetroShapeProvider; + provides org.apache.poi.extractor.ExtractorProvider with org.apache.poi.extractor.ole2.OLE2ScratchpadExtractorFactory; provides org.apache.poi.sl.usermodel.SlideShowProvider with org.apache.poi.hslf.usermodel.HSLFSlideShowFactory; provides org.apache.poi.sl.draw.ImageRenderer with org.apache.poi.hwmf.draw.HwmfImageRenderer, org.apache.poi.hemf.draw.HemfImageRenderer; diff --git a/poi-scratchpad/src/test/java9/module-info.class b/poi-scratchpad/src/test/java9/module-info.class index f5349bb241..91bc9fc6b8 100644 Binary files a/poi-scratchpad/src/test/java9/module-info.class and b/poi-scratchpad/src/test/java9/module-info.class differ diff --git a/poi-scratchpad/src/test/java9/module-info.java b/poi-scratchpad/src/test/java9/module-info.java index aa1e3d76c8..0d6ba80e5b 100644 --- a/poi-scratchpad/src/test/java9/module-info.java +++ b/poi-scratchpad/src/test/java9/module-info.java @@ -21,6 +21,8 @@ module org.apache.poi.scratchpad { requires commons.math3; requires org.apache.logging.log4j; + uses org.apache.poi.sl.usermodel.MetroShapeProvider; + provides org.apache.poi.extractor.ExtractorProvider with org.apache.poi.extractor.ole2.OLE2ScratchpadExtractorFactory; provides org.apache.poi.sl.usermodel.SlideShowProvider with org.apache.poi.hslf.usermodel.HSLFSlideShowFactory; provides org.apache.poi.sl.draw.ImageRenderer with org.apache.poi.hwmf.draw.HwmfImageRenderer, org.apache.poi.hemf.draw.HemfImageRenderer; diff --git a/poi/src/main/java/org/apache/poi/sl/usermodel/MetroShapeProvider.java b/poi/src/main/java/org/apache/poi/sl/usermodel/MetroShapeProvider.java new file mode 100644 index 0000000000..e89a14df56 --- /dev/null +++ b/poi/src/main/java/org/apache/poi/sl/usermodel/MetroShapeProvider.java @@ -0,0 +1,31 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.sl.usermodel; + +import java.io.IOException; + +import org.apache.poi.util.Internal; + +/** + * Experimental provider / loader for metro shapes. + * Metro shapes are xslf fragments stored in hslf shapes. + */ +@Internal +public interface MetroShapeProvider { + Shape parseShape(byte[] metroBytes) throws IOException; +} diff --git a/poi/src/test/java9/module-info.class b/poi/src/test/java9/module-info.class index 028b943d0a..438e778ab5 100644 Binary files a/poi/src/test/java9/module-info.class and b/poi/src/test/java9/module-info.class differ diff --git a/poi/src/test/java9/module-info.java b/poi/src/test/java9/module-info.java index 58ac2220c5..7b9cf162cf 100644 --- a/poi/src/test/java9/module-info.java +++ b/poi/src/test/java9/module-info.java @@ -24,6 +24,8 @@ module org.apache.poi.poi { requires java.logging; requires java.desktop; + uses org.apache.poi.sl.usermodel.MetroShapeProvider; + /* needed for CleanerUtil */ requires jdk.unsupported;