--- src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java (revision 1504246) +++ src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java (working copy) @@ -243,4 +243,294 @@ } + + /** + * ensure that font and color rich text attributes defined in a XSSFRichTextString + * are passed to XSSFSimpleShape. + * + * See Bugzilla 54969. + */ + public void testRichTextFontAndColor() { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + + XSSFTextBox shape = drawing.createTextbox(new XSSFClientAnchor(0, 0, 0, 0, 2, 2, 3, 4)); + XSSFRichTextString rt = new XSSFRichTextString("Test String"); + + XSSFFont font = wb.createFont(); + font.setColor(new XSSFColor(new Color(0, 128, 128))); + font.setFontName("Arial"); + rt.applyFont(font); + + shape.setText(rt); + + CTTextParagraph pr = shape.getCTShape().getTxBody().getPArray(0); + assertEquals(1, pr.sizeOfRArray()); + + CTTextCharacterProperties rPr = pr.getRArray(0).getRPr(); + assertEquals("Arial", rPr.getLatin().getTypeface()); + assertTrue(Arrays.equals( + new byte[]{0, (byte)128, (byte)128} , + rPr.getSolidFill().getSrgbClr().getVal())); + + } + + /** + * Test setText single paragraph to ensure backwards compatibility + */ + public void testSetTextSingleParagraph() { + + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + + XSSFTextBox shape = drawing.createTextbox(new XSSFClientAnchor(0, 0, 0, 0, 2, 2, 3, 4)); + XSSFRichTextString rt = new XSSFRichTextString("Test String"); + + XSSFFont font = wb.createFont(); + font.setColor(new XSSFColor(new Color(0, 255, 255))); + font.setFontName("Arial"); + rt.applyFont(font); + + shape.setText(rt); + + List paras = shape.getTextParagraphs(); + assertEquals(1, paras.size()); + assertEquals("Test String", paras.get(0).getText()); + + List runs = paras.get(0).getTextRuns(); + assertEquals(1, runs.size()); + assertEquals("Arial", runs.get(0).getFontFamily()); + + Color clr = runs.get(0).getFontColor(); + assertTrue(Arrays.equals( + new int[] { 0, 255, 255 } , + new int[] { clr.getRed(), clr.getGreen(), clr.getBlue() })); + } + + /** + * Test addNewTextParagraph + */ + public void testAddNewTextParagraph() { + + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + + XSSFTextBox shape = drawing.createTextbox(new XSSFClientAnchor(0, 0, 0, 0, 2, 2, 3, 4)); + + XSSFTextParagraph para = shape.addNewTextParagraph(); + para.addNewTextRun().setText("Line 1"); + + List paras = shape.getTextParagraphs(); + assertEquals(2, paras.size()); // this should be 2 as XSSFSimpleShape creates a default paragraph (no text), and then we add a string to that. + + List runs = para.getTextRuns(); + assertEquals(1, runs.size()); + assertEquals("Line 1", runs.get(0).getText()); + } + + /** + * Test addNewTextParagraph using RichTextString + */ + public void testAddNewTextParagraphWithRTS() { + + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + + XSSFTextBox shape = drawing.createTextbox(new XSSFClientAnchor(0, 0, 0, 0, 2, 2, 3, 4)); + XSSFRichTextString rt = new XSSFRichTextString("Test Rich Text String"); + + XSSFFont font = wb.createFont(); + font.setColor(new XSSFColor(new Color(0, 255, 255))); + font.setFontName("Arial"); + rt.applyFont(font); + + XSSFFont midfont = wb.createFont(); + midfont.setColor(new XSSFColor(new Color(0, 255, 0))); + rt.applyFont(5, 14, midfont); // set the text "Rich Text" to be green and the default font + + XSSFTextParagraph para = shape.addNewTextParagraph(rt); + + // Save and re-load it + wb = XSSFTestDataSamples.writeOutAndReadBack(wb); + sheet = wb.getSheetAt(0); + + // Check + drawing = sheet.createDrawingPatriarch(); + + List shapes = drawing.getShapes(); + assertEquals(1, shapes.size()); + assertTrue(shapes.get(0) instanceof XSSFSimpleShape); + + XSSFSimpleShape sshape = (XSSFSimpleShape) shapes.get(0); + + List paras = sshape.getTextParagraphs(); + assertEquals(2, paras.size()); // this should be 2 as XSSFSimpleShape creates a default paragraph (no text), and then we add a string to that. + + List runs = para.getTextRuns(); + assertEquals(3, runs.size()); + + // first run properties + assertEquals("Test ", runs.get(0).getText()); + assertEquals("Arial", runs.get(0).getFontFamily()); + + Color clr = runs.get(0).getFontColor(); + assertTrue(Arrays.equals( + new int[] { 0, 255, 255 } , + new int[] { clr.getRed(), clr.getGreen(), clr.getBlue() })); + + // second run properties + assertEquals("Rich Text", runs.get(1).getText()); + assertEquals(XSSFFont.DEFAULT_FONT_NAME, runs.get(1).getFontFamily()); + + clr = runs.get(1).getFontColor(); + assertTrue(Arrays.equals( + new int[] { 0, 255, 0 } , + new int[] { clr.getRed(), clr.getGreen(), clr.getBlue() })); + + // third run properties + assertEquals(" String", runs.get(2).getText()); + assertEquals("Arial", runs.get(2).getFontFamily()); + clr = runs.get(2).getFontColor(); + assertTrue(Arrays.equals( + new int[] { 0, 255, 255 } , + new int[] { clr.getRed(), clr.getGreen(), clr.getBlue() })); + } + + /** + * Test add multiple paragraphs and retrieve text + */ + public void testAddMultipleParagraphs() { + + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + + XSSFTextBox shape = drawing.createTextbox(new XSSFClientAnchor(0, 0, 0, 0, 2, 2, 3, 4)); + + XSSFTextParagraph para = shape.addNewTextParagraph(); + para.addNewTextRun().setText("Line 1"); + + para = shape.addNewTextParagraph(); + para.addNewTextRun().setText("Line 2"); + + para = shape.addNewTextParagraph(); + para.addNewTextRun().setText("Line 3"); + + List paras = shape.getTextParagraphs(); + assertEquals(4, paras.size()); // this should be 4 as XSSFSimpleShape creates a default paragraph (no text), and then we added 3 paragraphs + assertEquals("Line 1\nLine 2\nLine 3", shape.getText()); + } + + /** + * Test setting the text, then adding multiple paragraphs and retrieve text + */ + public void testSetAddMultipleParagraphs() { + + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + + XSSFTextBox shape = drawing.createTextbox(new XSSFClientAnchor(0, 0, 0, 0, 2, 2, 3, 4)); + + shape.setText("Line 1"); + + XSSFTextParagraph para = shape.addNewTextParagraph(); + para.addNewTextRun().setText("Line 2"); + + para = shape.addNewTextParagraph(); + para.addNewTextRun().setText("Line 3"); + + List paras = shape.getTextParagraphs(); + assertEquals(3, paras.size()); // this should be 3 as we overwrote the default paragraph with setText, then added 2 new paragraphs + assertEquals("Line 1\nLine 2\nLine 3", shape.getText()); + } + + /** + * Test reading text from a textbox in an existing file + */ + public void testReadTextBox(){ + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("WithDrawing.xlsx"); + XSSFSheet sheet = wb.getSheetAt(0); + //the sheet has one relationship and it is XSSFDrawing + List rels = sheet.getRelations(); + assertEquals(1, rels.size()); + assertTrue(rels.get(0) instanceof XSSFDrawing); + + XSSFDrawing drawing = (XSSFDrawing)rels.get(0); + //sheet.createDrawingPatriarch() should return the same instance of XSSFDrawing + assertSame(drawing, sheet.createDrawingPatriarch()); + String drawingId = drawing.getPackageRelationship().getId(); + + //there should be a relation to this drawing in the worksheet + assertTrue(sheet.getCTWorksheet().isSetDrawing()); + assertEquals(drawingId, sheet.getCTWorksheet().getDrawing().getId()); + + List shapes = drawing.getShapes(); + assertEquals(6, shapes.size()); + + assertTrue(shapes.get(4) instanceof XSSFSimpleShape); + + XSSFSimpleShape textbox = (XSSFSimpleShape) shapes.get(4); + assertEquals("Sheet with various pictures\n(jpeg, png, wmf, emf and pict)", textbox.getText()); + } + + /** + * Test reading multiple paragraphs from a textbox in an existing file + */ + public void testReadTextBoxParagraphs(){ + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("WithTextBox.xlsx"); + XSSFSheet sheet = wb.getSheetAt(0); + //the sheet has one relationship and it is XSSFDrawing + List rels = sheet.getRelations(); + assertEquals(1, rels.size()); + assertTrue(rels.get(0) instanceof XSSFDrawing); + + XSSFDrawing drawing = (XSSFDrawing)rels.get(0); + //sheet.createDrawingPatriarch() should return the same instance of XSSFDrawing + assertSame(drawing, sheet.createDrawingPatriarch()); + String drawingId = drawing.getPackageRelationship().getId(); + + //there should be a relation to this drawing in the worksheet + assertTrue(sheet.getCTWorksheet().isSetDrawing()); + assertEquals(drawingId, sheet.getCTWorksheet().getDrawing().getId()); + + List shapes = drawing.getShapes(); + assertEquals(1, shapes.size()); + + assertTrue(shapes.get(0) instanceof XSSFSimpleShape); + + XSSFSimpleShape textbox = (XSSFSimpleShape) shapes.get(0); + + List paras = textbox.getTextParagraphs(); + assertEquals(3, paras.size()); + + assertEquals("Line 2", paras.get(1).getText()); // check content of second paragraph + + assertEquals("Line 1\nLine 2\nLine 3", textbox.getText()); // check content of entire textbox + + // check attributes of paragraphs + assertEquals(TextAlign.LEFT, paras.get(0).getTextAlign()); + assertEquals(TextAlign.CENTER, paras.get(1).getTextAlign()); + assertEquals(TextAlign.RIGHT, paras.get(2).getTextAlign()); + + Color clr = paras.get(0).getTextRuns().get(0).getFontColor(); + assertTrue(Arrays.equals( + new int[] { 255, 0, 0 } , + new int[] { clr.getRed(), clr.getGreen(), clr.getBlue() })); + + clr = paras.get(1).getTextRuns().get(0).getFontColor(); + assertTrue(Arrays.equals( + new int[] { 0, 255, 0 } , + new int[] { clr.getRed(), clr.getGreen(), clr.getBlue() })); + + clr = paras.get(2).getTextRuns().get(0).getFontColor(); + assertTrue(Arrays.equals( + new int[] { 0, 0, 255 } , + new int[] { clr.getRed(), clr.getGreen(), clr.getBlue() })); + + } } --- src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextRun.java (revision 0) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextRun.java (revision 0) @@ -0,0 +1,433 @@ +/* ==================================================================== + 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.xssf.usermodel; + +import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType; +import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType; +import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal; + +import java.awt.Color; + +/** + * Represents a run of text within the containing text body. The run element is the + * lowest level text separation mechanism within a text body. + */ +public class XSSFTextRun { + private final CTRegularTextRun _r; + private final XSSFTextParagraph _p; + + XSSFTextRun(CTRegularTextRun r, XSSFTextParagraph p){ + _r = r; + _p = p; + } + + XSSFTextParagraph getParentParagraph(){ + return _p; + } + + public String getText(){ + return _r.getT(); + } + + public void setText(String text){ + _r.setT(text); + } + + public CTRegularTextRun getXmlObject(){ + return _r; + } + + public void setFontColor(Color color){ + CTTextCharacterProperties rPr = getRPr(); + CTSolidColorFillProperties fill = rPr.isSetSolidFill() ? rPr.getSolidFill() : rPr.addNewSolidFill(); + CTSRgbColor clr = fill.isSetSrgbClr() ? fill.getSrgbClr() : fill.addNewSrgbClr(); + clr.setVal(new byte[]{(byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue()}); + + if(fill.isSetHslClr()) fill.unsetHslClr(); + if(fill.isSetPrstClr()) fill.unsetPrstClr(); + if(fill.isSetSchemeClr()) fill.unsetSchemeClr(); + if(fill.isSetScrgbClr()) fill.unsetScrgbClr(); + if(fill.isSetSysClr()) fill.unsetSysClr(); + + } + + public Color getFontColor(){ + + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetSolidFill()){ + CTSolidColorFillProperties fill = rPr.getSolidFill(); + + if(fill.isSetSrgbClr()){ + CTSRgbColor clr = fill.getSrgbClr(); + byte[] rgb = clr.getVal(); + return new Color(0xFF & rgb[0], 0xFF & rgb[1], 0xFF & rgb[2]); + } + } + + return new Color(0, 0, 0); + } + + /** + * + * @param fontSize font size in points. + * The value of -1 unsets the Sz attribyte from the underlying xml bean + */ + public void setFontSize(double fontSize){ + CTTextCharacterProperties rPr = getRPr(); + if(fontSize == -1.0) { + if(rPr.isSetSz()) rPr.unsetSz(); + } else { + if(fontSize < 1.0) { + throw new IllegalArgumentException("Minimum font size is 1pt but was " + fontSize); + } + + rPr.setSz((int)(100*fontSize)); + } + } + + /** + * @return font size in points or -1 if font size is not set. + */ + public double getFontSize(){ + double scale = 1; + double size = XSSFFont.DEFAULT_FONT_SIZE; // default font size + CTTextNormalAutofit afit = getParentParagraph().getParentShape().getTxBody().getBodyPr().getNormAutofit(); + if(afit != null) scale = (double)afit.getFontScale() / 100000; + + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetSz()){ + size = rPr.getSz()*0.01; + } + + return size * scale; + } + + /** + * + * @return the spacing between characters within a text run, + * If this attribute is omitted than a value of 0 or no adjustment is assumed. + */ + public double getCharacterSpacing(){ + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetSpc()){ + return rPr.getSpc()*0.01; + } + return 0; + } + + /** + * Set the spacing between characters within a text run. + *

+ * The spacing is specified in points. Positive values will cause the text to expand, + * negative values to condense. + *

+ * + * @param spc character spacing in points. + */ + public void setCharacterSpacing(double spc){ + CTTextCharacterProperties rPr = getRPr(); + if(spc == 0.0) { + if(rPr.isSetSpc()) rPr.unsetSpc(); + } else { + rPr.setSpc((int)(100*spc)); + } + } + + /** + * Specifies the typeface, or name of the font that is to be used for this text run. + * + * @param typeface the font to apply to this text run. + * The value of null unsets the Typeface attrubute from the underlying xml. + */ + public void setFont(String typeface){ + setFontFamily(typeface, (byte)-1, (byte)-1, false); + } + + public void setFontFamily(String typeface, byte charset, byte pictAndFamily, boolean isSymbol){ + CTTextCharacterProperties rPr = getRPr(); + + if(typeface == null){ + if(rPr.isSetLatin()) rPr.unsetLatin(); + if(rPr.isSetCs()) rPr.unsetCs(); + if(rPr.isSetSym()) rPr.unsetSym(); + } else { + if(isSymbol){ + CTTextFont font = rPr.isSetSym() ? rPr.getSym() : rPr.addNewSym(); + font.setTypeface(typeface); + } else { + CTTextFont latin = rPr.isSetLatin() ? rPr.getLatin() : rPr.addNewLatin(); + latin.setTypeface(typeface); + if(charset != -1) latin.setCharset(charset); + if(pictAndFamily != -1) latin.setPitchFamily(pictAndFamily); + } + } + } + + /** + * @return font family or null if not set + */ + public String getFontFamily(){ + CTTextCharacterProperties rPr = getRPr(); + CTTextFont font = rPr.getLatin(); + if(font != null){ + return font.getTypeface(); + } + return XSSFFont.DEFAULT_FONT_NAME; + } + + public byte getPitchAndFamily(){ + CTTextCharacterProperties rPr = getRPr(); + CTTextFont font = rPr.getLatin(); + if(font != null){ + return font.getPitchFamily(); + } + return 0; + } + + /** + * Specifies whether a run of text will be formatted as strikethrough text. + * + * @param strike whether a run of text will be formatted as strikethrough text. + */ + public void setStrikethrough(boolean strike) { + getRPr().setStrike(strike ? STTextStrikeType.SNG_STRIKE : STTextStrikeType.NO_STRIKE); + } + + /** + * @return whether a run of text will be formatted as strikethrough text. Default is false. + */ + public boolean isStrikethrough() { + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetStrike()){ + return rPr.getStrike() != STTextStrikeType.NO_STRIKE; + } + return false; + } + + /** + * @return whether a run of text will be formatted as a superscript text. Default is false. + */ + public boolean isSuperscript() { + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetBaseline()){ + return rPr.getBaseline() > 0; + } + return false; + } + + /** + * Set the baseline for both the superscript and subscript fonts. + *

+ * The size is specified using a percentage. + * Positive values indicate superscript, negative values indicate subscript. + *

+ * + * @param baselineOffset + */ + public void setBaselineOffset(double baselineOffset){ + getRPr().setBaseline((int) baselineOffset * 1000); + } + + /** + * Set whether the text in this run is formatted as superscript. + * Default base line offset is 30% + * + * @see #setBaselineOffset(double) + */ + public void setSuperscript(boolean flag){ + setBaselineOffset(flag ? 30. : 0.); + } + + /** + * Set whether the text in this run is formatted as subscript. + * Default base line offset is -25%. + * + * @see #setBaselineOffset(double) + */ + public void setSubscript(boolean flag){ + setBaselineOffset(flag ? -25.0 : 0.); + } + + /** + * @return whether a run of text will be formatted as a superscript text. Default is false. + */ + public boolean isSubscript() { + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetBaseline()){ + return rPr.getBaseline() < 0; + } + return false; + } + + /** + * @return whether a run of text will be formatted as a superscript text. Default is false. + */ + public TextCap getTextCap() { + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetCap()){ + return TextCap.values()[rPr.getCap().intValue() - 1]; + } + return TextCap.NONE; + } + + /** + * Specifies whether this run of text will be formatted as bold text + * + * @param bold whether this run of text will be formatted as bold text + */ + public void setBold(boolean bold){ + getRPr().setB(bold); + } + + /** + * @return whether this run of text is formatted as bold text + */ + public boolean isBold(){ + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetB()){ + return rPr.getB(); + } + return false; + } + + /** + * @param italic whether this run of text is formatted as italic text + */ + public void setItalic(boolean italic){ + getRPr().setI(italic); + } + + /** + * @return whether this run of text is formatted as italic text + */ + public boolean isItalic(){ + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetI()){ + return rPr.getI(); + } + return false; + } + + /** + * @param underline whether this run of text is formatted as underlined text + */ + public void setUnderline(boolean underline) { + getRPr().setU(underline ? STTextUnderlineType.SNG : STTextUnderlineType.NONE); + } + + /** + * @return whether this run of text is formatted as underlined text + */ + public boolean isUnderline(){ + CTTextCharacterProperties rPr = getRPr(); + if(rPr.isSetU()){ + return rPr.getU() != STTextUnderlineType.NONE; + } + return false; + } + + protected CTTextCharacterProperties getRPr(){ + return _r.isSetRPr() ? _r.getRPr() : _r.addNewRPr(); + } + + @Override + public String toString(){ + return "[" + getClass() + "]" + getText(); + } +/* + public XSSFHyperlink createHyperlink(){ + XSSFHyperlink link = new XSSFHyperlink(_r.getRPr().addNewHlinkClick(), this); + return link; + } + + public XSSFHyperlink getHyperlink(){ + if(!_r.getRPr().isSetHlinkClick()) return null; + + + return new XSSFHyperlink(_r.getRPr().getHlinkClick()); + } + + private boolean fetchCharacterProperty(CharacterPropertyFetcher fetcher){ + boolean ok = false; + + if(_r.isSetRPr()) ok = fetcher.fetch(getRPr()); + + if(!ok) { + XSSFTextShape shape = _p.getParentShape(); + ok = shape.fetchShapeProperty(fetcher); + if(!ok){ + CTPlaceholder ph = shape.getCTPlaceholder(); + if(ph == null){ + // if it is a plain text box then take defaults from presentation.xml + XMLSlideShow ppt = shape.getSheet().getSlideShow(); + CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(_p.getLevel()); + if(themeProps != null) { + fetcher.isFetchingFromMaster = true; + ok = fetcher.fetch(themeProps); + } + } + if (!ok) { + CTTextParagraphProperties defaultProps = _p.getDefaultMasterStyle(); + if(defaultProps != null) { + fetcher.isFetchingFromMaster = true; + ok = fetcher.fetch(defaultProps); + } + } + } + } + + return ok; + } + + void copy(XSSFTextRun r){ + String srcFontFamily = r.getFontFamily(); + if(srcFontFamily != null && !srcFontFamily.equals(getFontFamily())){ + setFontFamily(srcFontFamily); + } + + Color srcFontColor = r.getFontColor(); + if(srcFontColor != null && !srcFontColor.equals(getFontColor())){ + setFontColor(srcFontColor); + } + + double srcFontSize = r.getFontSize(); + if(srcFontSize != getFontSize()){ + setFontSize(srcFontSize); + } + + boolean bold = r.isBold(); + if(bold != isBold()) setBold(bold); + + boolean italic = r.isItalic(); + if(italic != isItalic()) setItalic(italic); + + boolean underline = r.isUnderline(); + if(underline != isUnderline()) setUnderline(underline); + + boolean strike = r.isStrikethrough(); + if(strike != isStrikethrough()) setStrikethrough(strike); + }*/ +} --- src/ooxml/java/org/apache/poi/xssf/usermodel/TextCap.java (revision 0) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/TextCap.java (revision 0) @@ -0,0 +1,30 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Text Capitalization that is to be applied to the text run. This is a render-only + * modification and does not affect the actual characters stored in the text run. + */ +public enum TextCap { + NONE, + SMALL, + ALL +} --- src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSimpleShape.java (revision 1504246) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSimpleShape.java (working copy) @@ -17,6 +17,10 @@ package org.apache.poi.xssf.usermodel; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + import org.apache.poi.hssf.util.HSSFColor; import org.openxmlformats.schemas.drawingml.x2006.main.*; import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape; @@ -24,15 +28,19 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRElt; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRPrElt; import org.apache.poi.util.Internal; +import org.apache.poi.util.Units; +import org.apache.poi.ss.usermodel.VerticalAlignment; import org.openxmlformats.schemas.spreadsheetml.x2006.main.STUnderlineValues; /** * Represents a shape with a predefined geometry in a SpreadsheetML drawing. * Possible shape types are defined in {@link org.apache.poi.ss.usermodel.ShapeTypes} - * - * @author Yegor Kozlov */ -public class XSSFSimpleShape extends XSSFShape { // TODO - instantiable superclass +public class XSSFSimpleShape extends XSSFShape implements Iterable { // TODO - instantiable superclass + /** + * List of the paragraphs that make up the text in this shape + */ + private final List _paragraphs; /** * A default instance of CTShape used for creating new shapes. */ @@ -46,6 +54,15 @@ protected XSSFSimpleShape(XSSFDrawing drawing, CTShape ctShape) { this.drawing = drawing; this.ctShape = ctShape; + + _paragraphs = new ArrayList(); + + // initialize any existing paragraphs - this will be the default body paragraph in a new shape, + // or existing paragraphs that have been loaded from the file + CTTextBody body = ctShape.getTxBody(); + for(int i = 0; i < body.sizeOfPArray(); i++) { + _paragraphs.add(new XSSFTextParagraph(body.getPArray(i), ctShape)); + } } /** @@ -101,7 +118,7 @@ CTTextCharacterProperties endPr = p.addNewEndParaRPr(); endPr.setLang("en-US"); endPr.setSz(1100); - + body.addNewLstStyle(); prototype = shape; @@ -114,30 +131,48 @@ return ctShape; } + + public Iterator iterator(){ + return _paragraphs.iterator(); + } + /** - * Gets the shape type, one of the constants defined in {@link org.apache.poi.ss.usermodel.ShapeTypes}. - * - * @return the shape type - * @see org.apache.poi.ss.usermodel.ShapeTypes + * Returns the text from all paragraphs in the shape. Paragraphs are separated by new lines. + * + * @return text contained within this shape or empty string */ - public int getShapeType() { - return ctShape.getSpPr().getPrstGeom().getPrst().intValue(); + public String getText() { + StringBuilder out = new StringBuilder(); + for (XSSFTextParagraph p : _paragraphs) { + if (out.length() > 0) out.append('\n'); + out.append(p.getText()); + } + return out.toString(); } /** - * Sets the shape types. - * - * @param type the shape type, one of the constants defined in {@link org.apache.poi.ss.usermodel.ShapeTypes}. - * @see org.apache.poi.ss.usermodel.ShapeTypes + * Clear all text from this shape */ - public void setShapeType(int type) { - ctShape.getSpPr().getPrstGeom().setPrst(STShapeType.Enum.forInt(type)); + public void clearText(){ + _paragraphs.clear(); + CTTextBody txBody = ctShape.getTxBody(); + txBody.setPArray(null); // remove any existing paragraphs } + + /** + * Set a single paragraph of text on the shape. Note this will replace all existing paragraphs created on the shape. + * @param text string representing the paragraph text + */ + public void setText(String text){ + clearText(); - protected CTShapeProperties getShapeProperties(){ - return ctShape.getSpPr(); + addNewTextParagraph().addNewTextRun().setText(text); } + /** + * Set a single paragraph of text on the shape. Note this will replace all existing paragraphs created on the shape. + * @param str rich text string representing the paragraph text + */ public void setText(XSSFRichTextString str){ XSSFWorkbook wb = (XSSFWorkbook)getDrawing().getParent().getParent(); @@ -166,12 +201,415 @@ r.setT(lt.getT()); } } + + clearText(); ctShape.getTxBody().setPArray(new CTTextParagraph[]{p}); + _paragraphs.add(new XSSFTextParagraph(ctShape.getTxBody().getPArray(0), ctShape)); + } + + /** + * Returns a collection of the XSSFTextParagraphs that are attached to this shape + * + * @return text paragraphs in this shape + */ + public List getTextParagraphs() { + return _paragraphs; + } + /** + * Add a new paragraph run to this shape + * + * @return created paragraph run + */ + public XSSFTextParagraph addNewTextParagraph() { + CTTextBody txBody = ctShape.getTxBody(); + CTTextParagraph p = txBody.addNewP(); + XSSFTextParagraph paragraph = new XSSFTextParagraph(p, ctShape); + _paragraphs.add(paragraph); + return paragraph; + } + + /** + * Add a new paragraph run to this shape, set to the provided rich text string + * + * @return created paragraph run + */ + public XSSFTextParagraph addNewTextParagraph(XSSFRichTextString str) { + CTTextBody txBody = ctShape.getTxBody(); + CTTextParagraph p = txBody.addNewP(); + + if(str.numFormattingRuns() == 0){ + CTRegularTextRun r = p.addNewR(); + CTTextCharacterProperties rPr = r.addNewRPr(); + rPr.setLang("en-US"); + rPr.setSz(1100); + r.setT(str.getString()); + + } else { + for (int i = 0; i < str.getCTRst().sizeOfRArray(); i++) { + CTRElt lt = str.getCTRst().getRArray(i); + CTRPrElt ltPr = lt.getRPr(); + if(ltPr == null) ltPr = lt.addNewRPr(); + + CTRegularTextRun r = p.addNewR(); + CTTextCharacterProperties rPr = r.addNewRPr(); + rPr.setLang("en-US"); + + applyAttributes(ltPr, rPr); + + r.setT(lt.getT()); + } + } + + // Note: the XSSFTextParagraph constructor will create its required XSSFTextRuns from the provided CTTextParagraph + XSSFTextParagraph paragraph = new XSSFTextParagraph(p, ctShape); + _paragraphs.add(paragraph); + + return paragraph; } /** + * Sets the type of horizontal overflow for the text. * + * @param overflow - the type of horizontal overflow. + * A null values unsets this property. + */ + public void setTextHorizontalOverflow(TextHorizontalOverflow overflow){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(anchor == null) { + if(bodyPr.isSetHorzOverflow()) bodyPr.unsetHorzOverflow(); + } else { + bodyPr.setHorzOverflow(STTextHorzOverflowType.Enum.forInt(overflow.ordinal() + 1)); + } + } + } + + /** + * Returns the type of horizontal overflow for the text. + * + * @return the type of horizontal overflow + */ + public TextHorizontalOverflow getTextHorizontalOverflow(){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if(bodyPr != null) { + if(bodyPr.isSetHorzOverflow()){ + return TextHorizontalOverflow.values()[bodyPr.getVertOverflow().intValue() - 1]; + } + } + return TextHorizontalOverflow.OVERFLOW; + } + + /** + * Sets the type of vertical overflow for the text. + * + * @param overflow - the type of vertical overflow. + * A null values unsets this property. + */ + public void setTextVerticalOverflow(TextVerticalOverflow overflow){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(anchor == null) { + if(bodyPr.isSetVertOverflow()) bodyPr.unsetVertOverflow(); + } else { + bodyPr.setVertOverflow(STTextVertOverflowType.Enum.forInt(overflow.ordinal() + 1)); + } + } + } + + /** + * Returns the type of vertical overflow for the text. + * + * @return the type of vertical overflow + */ + public TextVerticalOverflow getTextVerticalOverflow(){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if(bodyPr != null) { + if(bodyPr.isSetVertOverflow()){ + return TextVerticalOverflow.values()[bodyPr.getVertOverflow().intValue() - 1]; + } + } + return TextVerticalOverflow.OVERFLOW; + } + + /** + * Sets the type of vertical alignment for the text within the shape. + * + * @param anchor - the type of alignment. + * A null values unsets this property. + */ + public void setVerticalAlignment(VerticalAlignment anchor){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(anchor == null) { + if(bodyPr.isSetAnchor()) bodyPr.unsetAnchor(); + } else { + bodyPr.setAnchor(STTextAnchoringType.Enum.forInt(anchor.ordinal() + 1)); + } + } + } + + /** + * Returns the type of vertical alignment for the text within the shape. + * + * @return the type of vertical alignment + */ + public VerticalAlignment getVerticalAlignment(){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if(bodyPr != null) { + if(bodyPr.isSetAnchor()){ + return VerticalAlignment.values()[bodyPr.getAnchor().intValue() - 1]; + } + } + return VerticalAlignment.TOP; + } + + /** + * Sets the vertical orientation of the text + * + * @param orientation vertical orientation of the text + * A null values unsets this property. + */ + public void setTextDirection(TextDirection orientation){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(orientation == null) { + if(bodyPr.isSetVert()) bodyPr.unsetVert(); + } else { + bodyPr.setVert(STTextVerticalType.Enum.forInt(orientation.ordinal() + 1)); + } + } + } + + /** + * Gets the vertical orientation of the text + * + * @return vertical orientation of the text + */ + public TextDirection getTextDirection(){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + STTextVerticalType.Enum val = bodyPr.getVert(); + if(val != null){ + return TextDirection.values()[val.intValue() - 1]; + } + } + return TextDirection.HORIZONTAL; + } + + + /** + * Returns the distance (in points) between the bottom of the text frame + * and the bottom of the inscribed rectangle of the shape that contains the text. + * + * @return the bottom inset in points + */ + public double getBottomInset(){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(bodyPr.isSetBIns()){ + return Units.toPoints(bodyPr.getBIns()); + } + } + // If this attribute is omitted, then a value of 0.05 inches is implied + return 3.6; + } + + /** + * Returns the distance (in points) between the left edge of the text frame + * and the left edge of the inscribed rectangle of the shape that contains + * the text. + * + * @return the left inset in points + */ + public double getLeftInset(){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(bodyPr.isSetLIns()){ + return Units.toPoints(bodyPr.getLIns()); + } + } + // If this attribute is omitted, then a value of 0.05 inches is implied + return 3.6; + } + + /** + * Returns the distance (in points) between the right edge of the + * text frame and the right edge of the inscribed rectangle of the shape + * that contains the text. + * + * @return the right inset in points + */ + public double getRightInset(){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(bodyPr.isSetRIns()){ + return Units.toPoints(bodyPr.getRIns()); + } + } + // If this attribute is omitted, then a value of 0.05 inches is implied + return 3.6; + } + + /** + * Returns the distance (in points) between the top of the text frame + * and the top of the inscribed rectangle of the shape that contains the text. + * + * @return the top inset in points + */ + public double getTopInset(){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(bodyPr.isSetTIns()){ + return Units.toPoints(bodyPr.getTIns()); + } + } + // If this attribute is omitted, then a value of 0.05 inches is implied + return 3.6; + } + + /** + * Sets the bottom inset. + * @see #getBottomInset() + * + * @param margin the bottom margin + */ + public void setBottomInset(double margin){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(margin == -1) bodyPr.unsetBIns(); + else bodyPr.setBIns(Units.toEMU(margin)); + } + } + + /** + * Sets the left inset. + * @see #getLeftInset() + * + * @param margin the left margin + */ + public void setLeftInset(double margin){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(margin == -1) bodyPr.unsetLIns(); + else bodyPr.setLIns(Units.toEMU(margin)); + } + } + + /** + * Sets the right inset. + * @see #getRightInset() + * + * @param margin the right margin + */ + public void setRightInset(double margin){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(margin == -1) bodyPr.unsetRIns(); + else bodyPr.setRIns(Units.toEMU(margin)); + } + } + + /** + * Sets the top inset. + * @see #getTopInset() + * + * @param margin the top margin + */ + public void setTopInset(double margin){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(margin == -1) bodyPr.unsetTIns(); + else bodyPr.setTIns(Units.toEMU(margin)); + } + } + + + /** + * @return whether to wrap words within the bounding rectangle + */ + public boolean getWordWrap(){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(bodyPr.isSetWrap()){ + return bodyPr.getWrap() == STTextWrappingType.SQUARE; + } + } + return true; + } + + /** + * + * @param wrap whether to wrap words within the bounding rectangle + */ + public void setWordWrap(boolean wrap){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + bodyPr.setWrap(wrap ? STTextWrappingType.SQUARE : STTextWrappingType.NONE); + } + } + + /** + * + * Specifies that a shape should be auto-fit to fully contain the text described within it. + * Auto-fitting is when text within a shape is scaled in order to contain all the text inside + * + * @param value type of autofit + */ + public void setTextAutofit(TextAutofit value){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(bodyPr.isSetSpAutoFit()) bodyPr.unsetSpAutoFit(); + if(bodyPr.isSetNoAutofit()) bodyPr.unsetNoAutofit(); + if(bodyPr.isSetNormAutofit()) bodyPr.unsetNormAutofit(); + + switch(value){ + case NONE: bodyPr.addNewNoAutofit(); break; + case NORMAL: bodyPr.addNewNormAutofit(); break; + case SHAPE: bodyPr.addNewSpAutoFit(); break; + } + } + } + + /** + * + * @return type of autofit + */ + public TextAutofit getTextAutofit(){ + CTTextBodyProperties bodyPr = ctShape.getTxBody().getBodyPr(); + if (bodyPr != null) { + if(bodyPr.isSetNoAutofit()) return TextAutofit.NONE; + else if (bodyPr.isSetNormAutofit()) return TextAutofit.NORMAL; + else if (bodyPr.isSetSpAutoFit()) return TextAutofit.SHAPE; + } + return TextAutofit.NORMAL; + } + + /** + * Gets the shape type, one of the constants defined in {@link org.apache.poi.ss.usermodel.ShapeTypes}. + * + * @return the shape type + * @see org.apache.poi.ss.usermodel.ShapeTypes + */ + public int getShapeType() { + return ctShape.getSpPr().getPrstGeom().getPrst().intValue(); + } + + /** + * Sets the shape types. + * + * @param type the shape type, one of the constants defined in {@link org.apache.poi.ss.usermodel.ShapeTypes}. + * @see org.apache.poi.ss.usermodel.ShapeTypes + */ + public void setShapeType(int type) { + ctShape.getSpPr().getPrstGeom().setPrst(STShapeType.Enum.forInt(type)); + } + + protected CTShapeProperties getShapeProperties(){ + return ctShape.getSpPr(); + } + + /** * org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRPrElt to * org.openxmlformats.schemas.drawingml.x2006.main.CTFont adapter */ @@ -186,8 +624,8 @@ } if(pr.sizeOfIArray() > 0) rPr.setI(pr.getIArray(0).getVal()); - if(pr.sizeOfFamilyArray() > 0) { - CTTextFont rFont = rPr.addNewLatin(); + if(pr.sizeOfRFontArray() > 0) { + CTTextFont rFont = rPr.isSetLatin() ? rPr.getLatin() : rPr.addNewLatin(); rFont.setTypeface(pr.getRFontArray(0).getVal()); } --- src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextParagraph.java (revision 0) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTextParagraph.java (revision 0) @@ -0,0 +1,553 @@ +/* ==================================================================== + 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.xssf.usermodel; + +import org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.apache.poi.util.Units; +import org.apache.xmlbeans.XmlObject; +import org.openxmlformats.schemas.drawingml.x2006.main.*; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Represents a paragraph of text within the containing text body. + * The paragraph is the highest level text separation mechanism. + */ +public class XSSFTextParagraph implements Iterable{ + private final CTTextParagraph _p; + private final CTShape _shape; + private final List _runs; + + XSSFTextParagraph(CTTextParagraph p, CTShape ctShape){ + _p = p; + _shape = ctShape; + _runs = new ArrayList(); + + for(XmlObject ch : _p.selectPath("*")){ + if(ch instanceof CTRegularTextRun){ + CTRegularTextRun r = (CTRegularTextRun)ch; + _runs.add(new XSSFTextRun(r, this)); + } else if (ch instanceof CTTextLineBreak){ + CTTextLineBreak br = (CTTextLineBreak)ch; + CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); + r.setRPr(br.getRPr()); + r.setT("\n"); + _runs.add(new XSSFTextRun(r, this)); + } else if (ch instanceof CTTextField){ + CTTextField f = (CTTextField)ch; + CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); + r.setRPr(f.getRPr()); + r.setT(f.getT()); + _runs.add(new XSSFTextRun(r, this)); + } + } + } + + public String getText(){ + StringBuilder out = new StringBuilder(); + for (XSSFTextRun r : _runs) { + out.append(r.getText()); + } + return out.toString(); + } + + @Internal + public CTTextParagraph getXmlObject(){ + return _p; + } + + @Internal + public CTShape getParentShape(){ + return _shape; + } + + public List getTextRuns(){ + return _runs; + } + + public Iterator iterator(){ + return _runs.iterator(); + } + + /** + * Add a new run of text + * + * @return a new run of text + */ + public XSSFTextRun addNewTextRun(){ + CTRegularTextRun r = _p.addNewR(); + CTTextCharacterProperties rPr = r.addNewRPr(); + rPr.setLang("en-US"); + XSSFTextRun run = new XSSFTextRun(r, this); + _runs.add(run); + return run; + } + + /** + * Insert a line break + * + * @return text run representing this line break ('\n') + */ + public XSSFTextRun addLineBreak(){ + CTTextLineBreak br = _p.addNewBr(); + CTTextCharacterProperties brProps = br.addNewRPr(); + if(_runs.size() > 0){ + // by default line break has the font size of the last text run + CTTextCharacterProperties prevRun = _runs.get(_runs.size() - 1).getRPr(); + brProps.set(prevRun); + } + CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); + r.setRPr(brProps); + r.setT("\n"); + XSSFTextRun run = new XSSFLineBreak(r, this, brProps); + _runs.add(run); + return run; + } + + /** + * Returns the alignment that is applied to the paragraph. + * + * If this attribute is omitted, then a value of left is implied. + * @return ??? alignment that is applied to the paragraph + */ + public TextAlign getTextAlign(){ + CTTextParagraphProperties pr = _p.getPPr(); + if(pr != null) { + return pr.isSetAlgn() ? TextAlign.values()[pr.getAlgn().intValue() - 1] : TextAlign.LEFT; + } + return TextAlign.LEFT; + } + + /** + * Specifies the alignment that is to be applied to the paragraph. + * Possible values for this include left, right, centered, justified and distributed, + * see {@link org.apache.poi.xssf.usermodel.TextAlign}. + * + * @param align text align + */ + public void setTextAlign(TextAlign align){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(align == null) { + if(pr.isSetAlgn()) pr.unsetAlgn(); + } else { + pr.setAlgn(STTextAlignType.Enum.forInt(align.ordinal() + 1)); + } + } + + /** + * Returns the font alignment that is applied to the paragraph. + * + * If this attribute is omitted, then a value of baseline is implied. + * @return ??? alignment that is applied to the paragraph + */ + public TextFontAlign getTextFontAlign(){ + CTTextParagraphProperties pr = _p.getPPr(); + if(pr != null) { + return pr.isSetFontAlgn() ? TextFontAlign.values()[pr.getFontAlgn().intValue() - 1] : TextFontAlign.BASELINE; + } + return TextFontAlign.BASELINE; + } + + /** + * Determines where vertically on a line of text the actual words are positioned. This deals + * with vertical placement of the characters with respect to the baselines. For instance + * having text anchored to the top baseline, anchored to the bottom baseline, centered in + * between, etc. + * + * @param align text font align + */ + public void setTextFontAlign(TextFontAlign align){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(align == null) { + if(pr.isSetFontAlgn()) pr.unsetFontAlgn(); + } else { + pr.setFontAlgn(STTextFontAlignType.Enum.forInt(align.ordinal() + 1)); + } + } + + + + /** + * Specifies the indent size that will be applied to the first line of text in the paragraph. + * + * @param value the indent in points. + */ + public void setIndent(double value){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(value == -1) { + if(pr.isSetIndent()) pr.unsetIndent(); + } else { + pr.setIndent(Units.toEMU(value)); + } + } + + /** + * + * @return the indent applied to the first line of text in the paragraph. + */ + public double getIndent(){ + CTTextParagraphProperties pr = _p.getPPr(); + if(pr == null) return 0; + + return Units.toPoints(pr.getIndent()); + } + + + + /** + * Specifies the left margin of the paragraph. This is specified in addition to the text body + * inset and applies only to this text paragraph. That is the text body Inset and the LeftMargin + * attributes are additive with respect to the text position. + * + * @param value the left margin of the paragraph + */ + public void setLeftMargin(double value){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(value == -1) { + if(pr.isSetMarL()) pr.unsetMarL(); + } else { + pr.setMarL(Units.toEMU(value)); + } + + } + + /** + * + * @return the left margin of the paragraph + */ + public double getLeftMargin(){ + CTTextParagraphProperties pr = _p.getPPr(); + if(pr != null && pr.isSetMarL()) { + return Units.toPoints(pr.getMarL()); + } + return 347663; // return the default which is 347663 + } + + /** + * Specifies the right margin of the paragraph. This is specified in addition to the text body + * inset and applies only to this text paragraph. That is the text body inset and the marR + * attributes are additive with respect to the text position. + * + * @param value the right margin of the paragraph + */ + public void setRightMargin(double value){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(value == -1) { + if(pr.isSetMarR()) pr.unsetMarR(); + } else { + pr.setMarR(Units.toEMU(value)); + } + + } + + /** + * + * @return the right margin of the paragraph + */ + public double getRightMargin(){ + CTTextParagraphProperties pr = _p.getPPr(); + if(pr != null && pr.isSetMarR()) { + return Units.toPoints(pr.getMarR()); + } + return 0; // return the default which is 0 + } + + /** + * Add a single tab stop to be used on a line of text when there are one or more tab characters + * present within the text. + * + * @param value the position of the tab stop relative to the left margin + */ + public void addTabStop(double value){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextTabStopList tabStops = pr.isSetTabLst() ? pr.getTabLst() : pr.addNewTabLst(); + tabStops.addNewTab().setPos(Units.toEMU(value)); + } + + /** + * This element specifies the vertical line spacing that is to be used within a paragraph. + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If linespacing >= 0, then linespacing is a percentage of normal line height + * If linespacing < 0, the absolute value of linespacing is the spacing in points + *

+ * Examples: + *

+     *      // spacing will be 120% of the size of the largest text on each line
+     *      paragraph.setLineSpacing(120);
+     *
+     *      // spacing will be 200% of the size of the largest text on each line
+     *      paragraph.setLineSpacing(200);
+     *
+     *      // spacing will be 48 points
+     *      paragraph.setLineSpacing(-48.0);
+     * 
+ * + * @param linespacing the vertical line spacing + */ + public void setLineSpacing(double linespacing){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); + if(linespacing >= 0) spc.addNewSpcPct().setVal((int)(linespacing*1000)); + else spc.addNewSpcPts().setVal((int)(-linespacing*100)); + pr.setLnSpc(spc); + } + + /** + * Returns the vertical line spacing that is to be used within a paragraph. + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If linespacing >= 0, then linespacing is a percentage of normal line height. + * If linespacing < 0, the absolute value of linespacing is the spacing in points + *

+ * + * @return the vertical line spacing. + */ + public double getLineSpacing(){ + CTTextParagraphProperties pr = _p.getPPr(); + double lnSpc = 100; // assume 100% default + if(pr != null) { + if(pr.isSetLnSpc()){ + CTTextSpacing spc = pr.getLnSpc(); + + if(spc.isSetSpcPct()) + lnSpc = spc.getSpcPct().getVal()*0.001; + else if (spc.isSetSpcPts()) + lnSpc = -spc.getSpcPts().getVal()*0.01; + } + } + + if(lnSpc > 0) { + // check if the percentage value is scaled + CTTextNormalAutofit normAutofit = getParentShape().getTxBody().getBodyPr().getNormAutofit(); + if(normAutofit != null) { + double scale = 1 - (double)normAutofit.getLnSpcReduction() / 100000; + lnSpc *= scale; + } + } + return lnSpc; + } + + /** + * Set the amount of vertical white space that will be present before the paragraph. + * This space is specified in either percentage or points: + *

+ * If spaceBefore >= 0, then space is a percentage of normal line height. + * If spaceBefore < 0, the absolute value of linespacing is the spacing in points + *

+ * Examples: + *

+     *      // The paragraph will be formatted to have a spacing before the paragraph text.
+     *      // The spacing will be 200% of the size of the largest text on each line
+     *      paragraph.setSpaceBefore(200);
+     *
+     *      // The spacing will be a size of 48 points
+     *      paragraph.setSpaceBefore(-48.0);
+     * 
+ * + * @param spaceBefore the vertical white space before the paragraph. + */ + public void setSpaceBefore(double spaceBefore){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); + if(spaceBefore >= 0) spc.addNewSpcPct().setVal((int)(spaceBefore*1000)); + else spc.addNewSpcPts().setVal((int)(-spaceBefore*100)); + pr.setSpcBef(spc); + } + + /** + * The amount of vertical white space before the paragraph + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If spaceBefore >= 0, then space is a percentage of normal line height. + * If spaceBefore < 0, the absolute value of linespacing is the spacing in points + *

+ * + * @return the vertical white space before the paragraph + */ + public double getSpaceBefore(){ + CTTextParagraphProperties pr = _p.getPPr(); + if(pr != null) { + if(pr.isSetSpcBef()) { + CTTextSpacing spc = pr.getSpcBef(); + + if(spc.isSetSpcPct()) + return spc.getSpcPct().getVal()*0.001; + else if (spc.isSetSpcPts()) + return -spc.getSpcPts().getVal()*0.01; + } + } + return 0; + } + + /** + * Set the amount of vertical white space that will be present after the paragraph. + * This space is specified in either percentage or points: + *

+ * If spaceAfter >= 0, then space is a percentage of normal line height. + * If spaceAfter < 0, the absolute value of linespacing is the spacing in points + *

+ * Examples: + *

+     *      // The paragraph will be formatted to have a spacing after the paragraph text.
+     *      // The spacing will be 200% of the size of the largest text on each line
+     *      paragraph.setSpaceAfter(200);
+     *
+     *      // The spacing will be a size of 48 points
+     *      paragraph.setSpaceAfter(-48.0);
+     * 
+ * + * @param spaceAfter the vertical white space after the paragraph. + */ + public void setSpaceAfter(double spaceAfter){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); + if(spaceAfter >= 0) spc.addNewSpcPct().setVal((int)(spaceAfter*1000)); + else spc.addNewSpcPts().setVal((int)(-spaceAfter*100)); + pr.setSpcAft(spc); + } + + /** + * The amount of vertical white space after the paragraph + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If spaceBefore >= 0, then space is a percentage of normal line height. + * If spaceBefore < 0, the absolute value of linespacing is the spacing in points + *

+ * + * @return the vertical white space after the paragraph + */ + public double getSpaceAfter(){ + CTTextParagraphProperties pr = _p.getPPr(); + if(pr != null) { + if(pr.isSetSpcAft()) { + CTTextSpacing spc = pr.getSpcAft(); + + if(spc.isSetSpcPct()) + return spc.getSpcPct().getVal()*0.001; + else if (spc.isSetSpcPts()) + return -spc.getSpcPts().getVal()*0.01; + } + } + return 0; + } + + /** + * Specifies the particular level text properties that this paragraph will follow. + * The value for this attribute formats the text according to the corresponding level + * paragraph properties defined in the SlideMaster. + * + * @param level the level (0 ... 4) + */ + public void setLevel(int level){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + + pr.setLvl(level); + } + + /** + * + * @return the text level of this paragraph (0-based). Default is 0. + */ + public int getLevel(){ + CTTextParagraphProperties pr = _p.getPPr(); + if(pr == null) return 0; + + return pr.getLvl(); + + } + + @Override + public String toString(){ + return "[" + getClass() + "]" + getText(); + } + +/* + void copy(XSLFTextParagraph p){ + TextAlign srcAlign = p.getTextAlign(); + if(srcAlign != getTextAlign()){ + setTextAlign(srcAlign); + } + + boolean isBullet = p.isBullet(); + if(isBullet != isBullet()){ + setBullet(isBullet); + if(isBullet) { + String buFont = p.getBulletFont(); + if(buFont != null && !buFont.equals(getBulletFont())){ + setBulletFont(buFont); + } + String buChar = p.getBulletCharacter(); + if(buChar != null && !buChar.equals(getBulletCharacter())){ + setBulletCharacter(buChar); + } + Color buColor = p.getBulletFontColor(); + if(buColor != null && !buColor.equals(getBulletFontColor())){ + setBulletFontColor(buColor); + } + double buSize = p.getBulletFontSize(); + if(buSize != getBulletFontSize()){ + setBulletFontSize(buSize); + } + } + } + + double leftMargin = p.getLeftMargin(); + if(leftMargin != getLeftMargin()){ + setLeftMargin(leftMargin); + } + + double indent = p.getIndent(); + if(indent != getIndent()){ + setIndent(indent); + } + + double spaceAfter = p.getSpaceAfter(); + if(spaceAfter != getSpaceAfter()){ + setSpaceAfter(spaceAfter); + } + double spaceBefore = p.getSpaceBefore(); + if(spaceBefore != getSpaceBefore()){ + setSpaceBefore(spaceBefore); + } + double lineSpacing = p.getLineSpacing(); + if(lineSpacing != getLineSpacing()){ + setLineSpacing(lineSpacing); + } + + List srcR = p.getTextRuns(); + List tgtR = getTextRuns(); + for(int i = 0; i < srcR.size(); i++){ + XSSFTextRun r1 = srcR.get(i); + XSSFTextRun r2 = tgtR.get(i); + r2.copy(r1); + } + }*/ + +} --- src/ooxml/java/org/apache/poi/xssf/usermodel/TextAutofit.java (revision 0) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/TextAutofit.java (revision 0) @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Specifies alist of auto-fit types. + *

+ * Autofit specofies that a shape should be auto-fit to fully contain the text described within it. + * Auto-fitting is when text within a shape is scaled in order to contain all the text inside + *

+ */ +public enum TextAutofit { + /** + * Specifies that text within the text body should not be auto-fit to the bounding box. + * Auto-fitting is when text within a text box is scaled in order to remain inside + * the text box. + */ + NONE, + /** + * Specifies that text within the text body should be normally auto-fit to the bounding box. + * Autofitting is when text within a text box is scaled in order to remain inside the text box. + * + *

+ * Example: Consider the situation where a user is building a diagram and needs + * to have the text for each shape that they are using stay within the bounds of the shape. + * An easy way this might be done is by using NORMAL autofit + *

+ */ + NORMAL, + /** + * Specifies that a shape should be auto-fit to fully contain the text described within it. + * Auto-fitting is when text within a shape is scaled in order to contain all the text inside. + * + *

+ * Example: Consider the situation where a user is building a diagram and needs to have + * the text for each shape that they are using stay within the bounds of the shape. + * An easy way this might be done is by using SHAPE autofit + *

+ */ + SHAPE +} --- src/ooxml/java/org/apache/poi/xssf/usermodel/TextHorizontalOverflow.java (revision 0) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/TextHorizontalOverflow.java (revision 0) @@ -0,0 +1,36 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Text Horizontal Overflow + */ +public enum TextHorizontalOverflow { + /** + * When a big character does not fit into a line, allow a + * horizontal overflow. + */ + OVERFLOW, + + /** + * When a big character does not fit into a line, clip it at + * the proper horizontal overflow. + */ + CLIP +} --- src/ooxml/java/org/apache/poi/xssf/usermodel/TextDirection.java (revision 0) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/TextDirection.java (revision 0) @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Vertical Text Types + */ +public enum TextDirection { + /** + * Horizontal text. This should be default. + */ + HORIZONTAL, + /** + * Vertical orientation. + * (each line is 90 degrees rotated clockwise, so it goes + * from top to bottom; each next line is to the left from + * the previous one). + */ + VERTICAL, + /** + * Vertical orientation. + * (each line is 270 degrees rotated clockwise, so it goes + * from bottom to top; each next line is to the right from + * the previous one). + */ + VERTICAL_270, + /** + * Determines if all of the text is vertical + * ("one letter on top of another"). + */ + STACKED; +} --- src/ooxml/java/org/apache/poi/xssf/usermodel/TextAlign.java (revision 0) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/TextAlign.java (revision 0) @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Specified a list of text alignment types + */ +public enum TextAlign { + /** + * Align text to the left margin. + */ + LEFT, + /** + * Align text in the center. + */ + CENTER, + + /** + * Align text to the right margin. + */ + RIGHT, + + /** + * Align text so that it is justified across the whole line. It + * is smart in the sense that it will not justify sentences + * which are short + */ + JUSTIFY, + JUSTIFY_LOW, + DIST, + THAI_DIST +} --- src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFLineBreak.java (revision 0) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFLineBreak.java (revision 0) @@ -0,0 +1,45 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; + +/** + * @author Darren Roberts + */ +class XSSFLineBreak extends XSSFTextRun { + private final CTTextCharacterProperties _brProps; + + XSSFLineBreak(CTRegularTextRun r, XSSFTextParagraph p, CTTextCharacterProperties brProps){ + super(r, p); + _brProps = brProps; + } + + @Override + protected CTTextCharacterProperties getRPr(){ + return _brProps; + } + + public void setText(String text){ + throw new IllegalStateException("You cannot change text of a line break, it is always '\\n'"); + } + +} --- src/ooxml/java/org/apache/poi/xssf/usermodel/TextFontAlign.java (revision 0) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/TextFontAlign.java (revision 0) @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Specified a list of text font alignment types + */ +public enum TextFontAlign { + /** + * Automatic alignment + */ + AUTO, + /** + * Align text to the top. + */ + TOP, + /** + * Align text in the center. + */ + CENTER, + + /** + * Align text to the baseline. + */ + BASELINE, + + /** + * Align text to the bottom. + */ + BOTTOM +} --- src/ooxml/java/org/apache/poi/xssf/usermodel/TextVerticalOverflow.java (revision 0) +++ src/ooxml/java/org/apache/poi/xssf/usermodel/TextVerticalOverflow.java (revision 0) @@ -0,0 +1,41 @@ +/* + * ==================================================================== + * 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.xssf.usermodel; + +/** + * Text Vertical Overflow + */ +public enum TextVerticalOverflow { + /** + * Overflow the text and pay no attention to top and bottom barriers. + */ + OVERFLOW, + + /** + * Pay attention to top and bottom barriers. Use an + * ellipsis to denote that there is text which is not visible. + */ + ELLIPSIS, + + /** + * Pay attention to top and bottom barriers. Provide no + * indication that there is text which is not visible. + */ + CLIP +}