TestXSLFTextShape.java

/* ====================================================================
   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.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;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeFalse;

import java.awt.Color;
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;
import org.apache.poi.xddf.usermodel.text.XDDFTextParagraph;
import org.apache.poi.xslf.XSLFTestDataSamples;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBodyProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;

class TestXSLFTextShape {
    private static boolean xslfOnly;

    @BeforeAll
    public static void checkHslf() {
        try {
            Class.forName("org.apache.poi.hslf.usermodel.HSLFSlideShow");
        } catch (Exception e) {
            xslfOnly = true;
        }
    }

    @Test
    void testLayouts() throws IOException {
        try (XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("layouts.pptx")) {

            List<XSLFSlide> 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));

        }
    }

    void verifySlide1(XSLFSlide slide){
        XSLFSlideLayout layout = slide.getSlideLayout();
        List<XSLFShape> shapes = slide.getShapes();
        assertEquals("Title Slide",layout.getName());

        XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0);
        XDDFTextBody tb1 = shape1.getTextBody();
        XDDFBodyProperties tbp1 = tb1.getBodyProperties();
        CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false);
        assertSame(STPlaceholderType.CTR_TITLE, ph1.getType());
        // anchor is not defined in the shape
        assertNull(getSpPr(shape1).getXfrm());

        XSLFTextShape masterShape1 = (XSLFTextShape)layout.getPlaceholder(ph1);
        assertNotNull(getSpPr(masterShape1).getXfrm());
        assertEquals(masterShape1.getAnchor(), shape1.getAnchor());

        CTTextBodyProperties bodyPr1 = shape1.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr1.isSetLIns() && !bodyPr1.isSetRIns() &&
                !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
                !bodyPr1.isSetAnchor()
        );
        assertEquals(7.2, shape1.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape1.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape1.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape1.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.MIDDLE, shape1.getVerticalAlignment());
        assertNull(tbp1.getLeftInset());
        assertNull(tbp1.getRightInset());
        assertNull(tbp1.getBottomInset());
        assertNull(tbp1.getTopInset());
        assertNull(tbp1.getAnchoring());

        // now check text properties
        assertEquals("Centered Title", shape1.getText());
        assertEquals("Centered Title",
            tb1.getParagraphs().stream().map(XDDFTextParagraph::getText).collect(Collectors.joining("\n")));
        XSLFTextRun r1 = shape1.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals("Calibri", r1.getFontFamily());
        assertEquals(44.0, r1.getFontSize(), 0);
        assertEquals(Color.black, getColor(r1.getFontColor()));

        XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1);
        XDDFTextBody tb2 = shape2.getTextBody();
        XDDFBodyProperties tbp2 = tb2.getBodyProperties();
        CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false);
        assertSame(STPlaceholderType.SUB_TITLE, ph2.getType());
        // anchor is not defined in the shape
        assertNull(getSpPr(shape2).getXfrm());

        XSLFTextShape masterShape2 = (XSLFTextShape)layout.getPlaceholder(ph2);
        assertNotNull(getSpPr(masterShape2).getXfrm());
        assertEquals(masterShape2.getAnchor(), shape2.getAnchor());

        CTTextBodyProperties bodyPr2 = shape2.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr2.isSetLIns() && !bodyPr2.isSetRIns() &&
                !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
                !bodyPr2.isSetAnchor()
        );
        assertEquals(7.2, shape2.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape2.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape2.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape2.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.TOP, shape2.getVerticalAlignment());
        assertNull(tbp2.getLeftInset());
        assertNull(tbp2.getRightInset());
        assertNull(tbp2.getBottomInset());
        assertNull(tbp2.getTopInset());
        assertNull(tbp2.getAnchoring());

        assertEquals("subtitle", shape2.getText());
        assertEquals("subtitle", tb2.getParagraphs().stream().map(XDDFTextParagraph::getText).collect(Collectors.joining("\n")));
        XSLFTextRun r2 = shape2.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals("Calibri", r2.getFontFamily());
        assertEquals(32.0, r2.getFontSize(), 0);
        // TODO fix calculation of tint
        //assertEquals(new Color(137, 137, 137), r2.getFontColor());
    }

    void verifySlide2(XSLFSlide slide){
        XSLFSlideLayout layout = slide.getSlideLayout();
        List<XSLFShape> shapes = slide.getShapes();
        assertEquals("Title and Content",layout.getName());

        XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0);
        CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false);
        assertSame(STPlaceholderType.TITLE, ph1.getType());
        // anchor is not defined in the shape
        assertNull(getSpPr(shape1).getXfrm());

        XSLFTextShape masterShape1 = (XSLFTextShape)layout.getPlaceholder(ph1);
        // layout does not have anchor info either, it is in the slide master
        assertNull(getSpPr(masterShape1).getXfrm());
        masterShape1 = (XSLFTextShape)layout.getSlideMaster().getPlaceholder(ph1);
        assertNotNull(getSpPr(masterShape1).getXfrm());
        assertEquals(masterShape1.getAnchor(), shape1.getAnchor());

        CTTextBodyProperties bodyPr1 = shape1.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr1.isSetLIns() && !bodyPr1.isSetRIns() &&
                !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
                !bodyPr1.isSetAnchor()
        );
        assertEquals(7.2, shape1.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape1.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape1.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape1.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.MIDDLE, shape1.getVerticalAlignment());

        // now check text properties
        assertEquals("Title", shape1.getText());
        XSLFTextRun r1 = shape1.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals("Calibri", r1.getFontFamily());
        assertEquals(44.0, r1.getFontSize(), 0);
        assertEquals(Color.black, getColor(r1.getFontColor()));

        XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1);
        CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false);
        assertFalse(ph2.isSetType()); // <p:ph idx="1"/>
        assertTrue(ph2.isSetIdx());
        assertEquals(1, ph2.getIdx());
        // anchor is not defined in the shape
        assertNull(getSpPr(shape2).getXfrm());

        XSLFTextShape masterShape2 = (XSLFTextShape)layout.getPlaceholder(ph2);
        // anchor of the body text is missing in the slide layout, llokup in the slide master
        assertNull(getSpPr(masterShape2).getXfrm());
        masterShape2 = (XSLFTextShape)layout.getSlideMaster().getPlaceholder(ph2);
        assertNotNull(getSpPr(masterShape2).getXfrm());
        assertEquals(masterShape2.getAnchor(), shape2.getAnchor());

        CTTextBodyProperties bodyPr2 = shape2.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr2.isSetLIns() && !bodyPr2.isSetRIns() &&
                !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
                !bodyPr2.isSetAnchor()
        );
        assertEquals(7.2, shape2.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape2.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape2.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape2.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.TOP, shape2.getVerticalAlignment());

        XSLFTextRun pr1 = shape2.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(0, pr1.getParagraph().getIndentLevel());
        assertEquals("Content", pr1.getRawText());
        assertEquals("Calibri", pr1.getFontFamily());
        assertEquals(32.0, pr1.getFontSize(), 0);
        assertEquals(27.0, pr1.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2022", pr1.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr1.getParagraph().getBulletFont());

        XSLFTextRun pr2 = shape2.getTextParagraphs().get(1).getTextRuns().get(0);
        assertEquals(1, pr2.getParagraph().getIndentLevel());
        assertEquals("Level 2", pr2.getRawText());
        assertEquals("Calibri", pr2.getFontFamily());
        assertEquals(28.0, pr2.getFontSize(), 0);
        assertEquals(58.5, pr2.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2013", pr2.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr2.getParagraph().getBulletFont());

        XSLFTextRun pr3 = shape2.getTextParagraphs().get(2).getTextRuns().get(0);
        assertEquals(2, pr3.getParagraph().getIndentLevel());
        assertEquals("Level 3", pr3.getRawText());
        assertEquals("Calibri", pr3.getFontFamily());
        assertEquals(24.0, pr3.getFontSize(), 0);
        assertEquals(90.0, pr3.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2022", pr3.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr3.getParagraph().getBulletFont());

        XSLFTextRun pr4 = shape2.getTextParagraphs().get(3).getTextRuns().get(0);
        assertEquals(3, pr4.getParagraph().getIndentLevel());
        assertEquals("Level 4", pr4.getRawText());
        assertEquals("Calibri", pr4.getFontFamily());
        assertEquals(20.0, pr4.getFontSize(), 0);
        assertEquals(126.0, pr4.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2013", pr4.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr4.getParagraph().getBulletFont());

        XSLFTextRun pr5 = shape2.getTextParagraphs().get(4).getTextRuns().get(0);
        assertEquals(4, pr5.getParagraph().getIndentLevel());
        assertEquals("Level 5", pr5.getRawText());
        assertEquals("Calibri", pr5.getFontFamily());
        assertEquals(20.0, pr5.getFontSize(), 0);
        assertEquals(162.0, pr5.getParagraph().getLeftMargin(), 0);
        assertEquals("\u00bb", pr5.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr5.getParagraph().getBulletFont());

    }

    void verifySlide3(XSLFSlide slide){
        XSLFSlideLayout layout = slide.getSlideLayout();
        List<XSLFShape> shapes = slide.getShapes();
        assertEquals("Section Header",layout.getName());

        XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0);
        CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false);
        assertSame(STPlaceholderType.TITLE, ph1.getType());
        // anchor is not defined in the shape
        assertNull(getSpPr(shape1).getXfrm());

        XSLFTextShape masterShape1 = (XSLFTextShape)layout.getPlaceholder(ph1);
        assertNotNull(getSpPr(masterShape1).getXfrm());
        assertEquals(masterShape1.getAnchor(), shape1.getAnchor());

        CTTextBodyProperties bodyPr1 = shape1.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr1.isSetLIns() && !bodyPr1.isSetRIns() &&
                !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
                !bodyPr1.isSetAnchor()
        );
        assertEquals(7.2, shape1.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape1.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape1.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape1.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.TOP, shape1.getVerticalAlignment());

        // now check text properties
        assertEquals("Section Title", shape1.getText());
        XSLFTextRun r1 = shape1.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(TextAlign.LEFT, r1.getParagraph().getTextAlign());
        assertEquals("Calibri", r1.getFontFamily());
        assertEquals(40.0, r1.getFontSize(), 0);
        assertEquals(Color.black, getColor(r1.getFontColor()));
        assertTrue(r1.isBold());
        assertFalse(r1.isItalic());
        assertFalse(r1.isUnderlined());

        XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1);
        CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false);
        assertSame(STPlaceholderType.BODY, ph2.getType());
        // anchor is not defined in the shape
        assertNull(getSpPr(shape2).getXfrm());

        XSLFTextShape masterShape2 = (XSLFTextShape)layout.getPlaceholder(ph2);
        assertNotNull(getSpPr(masterShape2).getXfrm());
        assertEquals(masterShape2.getAnchor(), shape2.getAnchor());

        CTTextBodyProperties bodyPr2 = shape2.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr2.isSetLIns() && !bodyPr2.isSetRIns() &&
                !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
                !bodyPr2.isSetAnchor()
        );
        assertEquals(7.2, shape2.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape2.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape2.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape2.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.BOTTOM, shape2.getVerticalAlignment());

        assertEquals("Section Header", shape2.getText());
        XSLFTextRun r2 = shape2.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(TextAlign.LEFT, r2.getParagraph().getTextAlign());
        assertEquals("Calibri", r2.getFontFamily());
        assertEquals(20.0, r2.getFontSize(), 0);
        // TODO fix calculation of tint
        //assertEquals(new Color(137, 137, 137), r2.getFontColor());
    }

    void verifySlide4(XSLFSlide slide){
        XSLFSlideLayout layout = slide.getSlideLayout();
        List<XSLFShape> shapes = slide.getShapes();
        assertEquals("Two Content",layout.getName());

        XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0);
        CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false);
        assertSame(STPlaceholderType.TITLE, ph1.getType());
        // anchor is not defined in the shape
        assertNull(getSpPr(shape1).getXfrm());

        XSLFTextShape masterShape1 = (XSLFTextShape)layout.getPlaceholder(ph1);
        // layout does not have anchor info either, it is in the slide master
        assertNull(getSpPr(masterShape1).getXfrm());
        masterShape1 = (XSLFTextShape)layout.getSlideMaster().getPlaceholder(ph1);
        assertNotNull(getSpPr(masterShape1).getXfrm());
        assertEquals(masterShape1.getAnchor(), shape1.getAnchor());

        CTTextBodyProperties bodyPr1 = shape1.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr1.isSetLIns() && !bodyPr1.isSetRIns() &&
                !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
                !bodyPr1.isSetAnchor()
        );
        assertEquals(7.2, shape1.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape1.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape1.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape1.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.MIDDLE, shape1.getVerticalAlignment());

        // now check text properties
        assertEquals("Title", shape1.getText());
        XSLFTextRun r1 = shape1.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(TextAlign.CENTER, r1.getParagraph().getTextAlign());
        assertEquals("Calibri", r1.getFontFamily());
        assertEquals(44.0, r1.getFontSize(), 0);
        assertEquals(Color.black, getColor(r1.getFontColor()));

        XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1);
        CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false);
        assertFalse(ph2.isSetType());
        assertTrue(ph2.isSetIdx());
        assertEquals(1, ph2.getIdx());  //<p:ph sz="half" idx="1"/>
        // anchor is not defined in the shape
        assertNull(getSpPr(shape2).getXfrm());

        XSLFTextShape masterShape2 = (XSLFTextShape)layout.getPlaceholder(ph2);
        assertNotNull(getSpPr(masterShape2).getXfrm());
        assertEquals(masterShape2.getAnchor(), shape2.getAnchor());

        CTTextBodyProperties bodyPr2 = shape2.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr2.isSetLIns() && !bodyPr2.isSetRIns() &&
                !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
                !bodyPr2.isSetAnchor()
        );
        assertEquals(7.2, shape2.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape2.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape2.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape2.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.TOP, shape2.getVerticalAlignment());

        XSLFTextRun pr1 = shape2.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(0, pr1.getParagraph().getIndentLevel());
        assertEquals("Left", pr1.getRawText());
        assertEquals("Calibri", pr1.getFontFamily());
        assertEquals(28.0, pr1.getFontSize(), 0);
        assertEquals(27.0, pr1.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2022", pr1.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr1.getParagraph().getBulletFont());

        XSLFTextRun pr2 = shape2.getTextParagraphs().get(1).getTextRuns().get(0);
        assertEquals(1, pr2.getParagraph().getIndentLevel());
        assertEquals("Level 2", pr2.getParagraph().getText());
        assertEquals("Calibri", pr2.getFontFamily());
        assertEquals(24.0, pr2.getFontSize(), 0);
        assertEquals(58.5, pr2.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2013", pr2.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr2.getParagraph().getBulletFont());

        XSLFTextRun pr3 = shape2.getTextParagraphs().get(2).getTextRuns().get(0);
        assertEquals(2, pr3.getParagraph().getIndentLevel());
        assertEquals("Level 3", pr3.getParagraph().getText());
        assertEquals("Calibri", pr3.getFontFamily());
        assertEquals(20.0, pr3.getFontSize(), 0);
        assertEquals(90.0, pr3.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2022", pr3.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr3.getParagraph().getBulletFont());

        XSLFTextRun pr4 = shape2.getTextParagraphs().get(3).getTextRuns().get(0);
        assertEquals(3, pr4.getParagraph().getIndentLevel());
        assertEquals("Level 4", pr4.getParagraph().getText());
        assertEquals("Calibri", pr4.getFontFamily());
        assertEquals(18.0, pr4.getFontSize(), 0);
        assertEquals(126.0, pr4.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2013", pr4.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr4.getParagraph().getBulletFont());

        XSLFTextShape shape3 = (XSLFTextShape)shapes.get(2);
        XSLFTextRun pr5 = shape3.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(0, pr5.getParagraph().getIndentLevel());
        assertEquals("Right", pr5.getRawText());
        assertEquals("Calibri", pr5.getFontFamily());
        assertEquals(Color.black, getColor(pr5.getFontColor()));
    }

    @SuppressWarnings("unused")
    void verifySlide5(XSLFSlide slide){
        XSLFSlideLayout layout = slide.getSlideLayout();
        List<XSLFShape> shapes = slide.getShapes();
        // TODO
    }

    void verifySlide7(XSLFSlide slide){
        XSLFSlideLayout layout = slide.getSlideLayout();
        List<XSLFShape> shapes = slide.getShapes();
        assertEquals("Blank",layout.getName());

        XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0);
        CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false);
        assertSame(STPlaceholderType.TITLE, ph1.getType());
        // anchor is not defined in the shape
        assertNull(getSpPr(shape1).getXfrm());

        CTTextBodyProperties bodyPr1 = shape1.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr1.isSetLIns() && !bodyPr1.isSetRIns() &&
                !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
                !bodyPr1.isSetAnchor()
        );
        assertEquals(7.2, shape1.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape1.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape1.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape1.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.MIDDLE, shape1.getVerticalAlignment());

        // now check text properties
        assertEquals("Blank with Default Title", shape1.getText());
        XSLFTextRun r1 = shape1.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(TextAlign.CENTER, r1.getParagraph().getTextAlign());
        assertEquals("Calibri", r1.getFontFamily());
        assertEquals(44.0, r1.getFontSize(), 0);
        assertEquals(Color.black, getColor(r1.getFontColor()));
        assertFalse(r1.isBold());

        XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1);

        CTTextBodyProperties bodyPr2 = shape2.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr2.isSetLIns() && !bodyPr2.isSetRIns() &&
                !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
                !bodyPr2.isSetAnchor()
        );
        assertEquals(7.2, shape2.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape2.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape2.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape2.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.TOP, shape2.getVerticalAlignment());

        XSLFTextRun pr1 = shape2.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(0, pr1.getParagraph().getIndentLevel());
        assertEquals("Default Text", pr1.getRawText());
        assertEquals("Calibri", pr1.getFontFamily());
        assertEquals(18.0, pr1.getFontSize(), 0);

        XSLFTextShape shape3 = (XSLFTextShape)shapes.get(2);
        assertEquals("Default", shape3.getTextParagraphs().get(0).getText());
        assertEquals("Text with levels", shape3.getTextParagraphs().get(1).getText());
        assertEquals("Level 1", shape3.getTextParagraphs().get(2).getText());
        assertEquals("Level 2", shape3.getTextParagraphs().get(3).getText());
        assertEquals("Level 3", shape3.getTextParagraphs().get(4).getText());

        for(int p = 0; p < 5; p++) {
            XSLFTextParagraph pr = shape3.getTextParagraphs().get(p);
            assertEquals("Calibri", pr.getTextRuns().get(0).getFontFamily());
            assertEquals(18.0, pr.getTextRuns().get(0).getFontSize(), 0);
        }
    }

    void verifySlide8(XSLFSlide slide){
        XSLFSlideLayout layout = slide.getSlideLayout();
        List<XSLFShape> shapes = slide.getShapes();
        assertEquals("Content with Caption",layout.getName());

        XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0);
        CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false);
        assertSame(STPlaceholderType.TITLE, ph1.getType());
        // anchor is not defined in the shape
        assertNull(getSpPr(shape1).getXfrm());

        XSLFTextShape masterShape1 = (XSLFTextShape)layout.getPlaceholder(ph1);
        // layout does not have anchor info either, it is in the slide master
        assertNotNull(getSpPr(masterShape1).getXfrm());
        assertEquals(masterShape1.getAnchor(), shape1.getAnchor());

        CTTextBodyProperties bodyPr1 = shape1.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr1.isSetLIns() && !bodyPr1.isSetRIns() &&
                !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
                !bodyPr1.isSetAnchor()
        );
        assertEquals(7.2, shape1.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape1.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape1.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape1.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.BOTTOM, shape1.getVerticalAlignment());

        // now check text properties
        assertEquals("Caption", shape1.getText());
        XSLFTextRun r1 = shape1.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(TextAlign.LEFT, r1.getParagraph().getTextAlign());
        assertEquals("Calibri", r1.getFontFamily());
        assertEquals(20.0, r1.getFontSize(), 0);
        assertEquals(Color.black, getColor(r1.getFontColor()));
        assertTrue(r1.isBold());

        XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1);
        CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false);
        assertFalse(ph2.isSetType());
        assertTrue(ph2.isSetIdx());
        assertEquals(1, ph2.getIdx());
        // anchor is not defined in the shape
        assertNull(getSpPr(shape2).getXfrm());

        XSLFTextShape masterShape2 = (XSLFTextShape)layout.getPlaceholder(ph2);
        assertNotNull(getSpPr(masterShape2).getXfrm());
        assertEquals(masterShape2.getAnchor(), shape2.getAnchor());

        CTTextBodyProperties bodyPr2 = shape2.getTextBodyPr();
        // none of the following properties are set in the shapes and fetched from the master shape
        assertTrue(
                !bodyPr2.isSetLIns() && !bodyPr2.isSetRIns() &&
                !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
                !bodyPr2.isSetAnchor()
        );
        assertEquals(7.2, shape2.getLeftInset(), 0);  // 0.1"
        assertEquals(7.2, shape2.getRightInset(), 0); // 0.1"
        assertEquals(3.6, shape2.getTopInset(), 0);  // 0.05"
        assertEquals(3.6, shape2.getBottomInset(), 0); // 0.05"
        assertEquals(VerticalAlignment.TOP, shape2.getVerticalAlignment());

        XSLFTextRun pr1 = shape2.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(0, pr1.getParagraph().getIndentLevel());
        assertEquals("Level 1", pr1.getRawText());
        assertEquals("Calibri", pr1.getFontFamily());
        assertEquals(32.0, pr1.getFontSize(), 0);
        assertEquals(27.0, pr1.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2022", pr1.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr1.getParagraph().getBulletFont());

        XSLFTextRun pr2 = shape2.getTextParagraphs().get(1).getTextRuns().get(0);
        assertEquals(1, pr2.getParagraph().getIndentLevel());
        assertEquals("Level 2", pr2.getParagraph().getText());
        assertEquals("Calibri", pr2.getFontFamily());
        assertEquals(28.0, pr2.getFontSize(), 0);
        assertEquals(58.5, pr2.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2013", pr2.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr2.getParagraph().getBulletFont());

        XSLFTextRun pr3 = shape2.getTextParagraphs().get(2).getTextRuns().get(0);
        assertEquals(2, pr3.getParagraph().getIndentLevel());
        assertEquals("Level 3", pr3.getParagraph().getText());
        assertEquals("Calibri", pr3.getFontFamily());
        assertEquals(24.0, pr3.getFontSize(), 0);
        assertEquals(90.0, pr3.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2022", pr3.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr3.getParagraph().getBulletFont());

        XSLFTextRun pr4 = shape2.getTextParagraphs().get(3).getTextRuns().get(0);
        assertEquals(3, pr4.getParagraph().getIndentLevel());
        assertEquals("Level 4", pr4.getParagraph().getText());
        assertEquals("Calibri", pr4.getFontFamily());
        assertEquals(20.0, pr4.getFontSize(), 0);
        assertEquals(126.0, pr4.getParagraph().getLeftMargin(), 0);
        assertEquals("\u2013", pr4.getParagraph().getBulletCharacter());
        assertEquals("Arial", pr4.getParagraph().getBulletFont());

        XSLFTextShape shape3 = (XSLFTextShape)shapes.get(2);
        assertEquals(VerticalAlignment.TOP, shape3.getVerticalAlignment());
        assertEquals("Content with caption", shape3.getText());

        pr1 = shape3.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(0, pr1.getParagraph().getIndentLevel());
        assertEquals("Content with caption", pr1.getRawText());
        assertEquals("Calibri", pr1.getFontFamily());
        assertEquals(14.0, pr1.getFontSize(), 0);

    }

    void verifySlide10(XSLFSlide slide){
        XSLFTextShape footer = (XSLFTextShape)slide.getPlaceholderByType(STPlaceholderType.INT_FTR);

        // now check text properties
        assertEquals("Apache Software Foundation", footer.getText());
        assertEquals(VerticalAlignment.MIDDLE, footer.getVerticalAlignment());

        XSLFTextRun r1 = footer.getTextParagraphs().get(0).getTextRuns().get(0);
        assertEquals(TextAlign.CENTER, r1.getParagraph().getTextAlign());
        assertEquals("Calibri", r1.getFontFamily());
        assertEquals(12.0, r1.getFontSize(), 0);
        assertEquals(new Color(0x898989), getColor(r1.getFontColor()));

        XSLFTextShape dt = (XSLFTextShape)slide.getPlaceholderByType(STPlaceholderType.INT_DT);
        assertEquals("Friday, October 21, 2011", dt.getText());

        XSLFTextShape sldNum = (XSLFTextShape)slide.getPlaceholderByType(STPlaceholderType.INT_SLD_NUM);
        assertEquals("10", sldNum.getText());
    }

    @Test
    void testTitleStyles() throws IOException {
        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 {
        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);
        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<? extends TextParagraph<?, ?, ? extends TextRun>>) 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);
        }
    }
}