diff --git a/build.xml b/build.xml index 2eddf722f5..f40ff4c774 100644 --- a/build.xml +++ b/build.xml @@ -40,7 +40,7 @@ under the License. The Apache POI project Ant build. - + @@ -283,13 +283,10 @@ under the License. NOTE: we did not update to 3.x yet because it requires Java 7, but we are still supporting Java 6 currently =========================================================================================================== --> - - + + - - - - + @@ -441,7 +438,7 @@ under the License. This is POI ${version.id} - Java Version ${ant.java.version} + Java Version ${ant.java.version}/${java.version} Timestamp ${DSTAMP} The main targets of interest are: - clean Erase all build work products (ie. everything in the build directory) @@ -480,7 +477,9 @@ under the License. + + var rel = ("REL_"+project.getProperty("version.id")).toUpperCase().replace(/\W/g,"_"); diff --git a/sonar/examples/pom.xml b/sonar/examples/pom.xml index e0e6a24a13..cac1dc446c 100644 --- a/sonar/examples/pom.xml +++ b/sonar/examples/pom.xml @@ -6,7 +6,7 @@ org.apache.poi poi-parent - 3.15-beta3-SNAPSHOT + 3.15-beta4-SNAPSHOT poi-examples jar diff --git a/sonar/excelant/pom.xml b/sonar/excelant/pom.xml index db1a4513dc..2cbeec7280 100644 --- a/sonar/excelant/pom.xml +++ b/sonar/excelant/pom.xml @@ -6,7 +6,7 @@ org.apache.poi poi-parent - 3.15-beta3-SNAPSHOT + 3.15-beta4-SNAPSHOT poi-excelant jar diff --git a/sonar/main/pom.xml b/sonar/main/pom.xml index ffc9499d96..26a8f2ba40 100644 --- a/sonar/main/pom.xml +++ b/sonar/main/pom.xml @@ -6,7 +6,7 @@ org.apache.poi poi-parent - 3.15-beta3-SNAPSHOT + 3.15-beta4-SNAPSHOT poi-main jar diff --git a/sonar/ooxml-schema-encryption/pom.xml b/sonar/ooxml-schema-encryption/pom.xml index 1366a2bad8..d704a6525d 100644 --- a/sonar/ooxml-schema-encryption/pom.xml +++ b/sonar/ooxml-schema-encryption/pom.xml @@ -6,7 +6,7 @@ org.apache.poi poi-parent - 3.15-beta3-SNAPSHOT + 3.15-beta4-SNAPSHOT .. poi-ooxml-schema-encryption diff --git a/sonar/ooxml-schema-security/pom.xml b/sonar/ooxml-schema-security/pom.xml index 336bf88fb7..7aae600f41 100644 --- a/sonar/ooxml-schema-security/pom.xml +++ b/sonar/ooxml-schema-security/pom.xml @@ -6,7 +6,7 @@ org.apache.poi poi-parent - 3.15-beta3-SNAPSHOT + 3.15-beta4-SNAPSHOT .. poi-ooxml-schema-security diff --git a/sonar/ooxml-schema/pom.xml b/sonar/ooxml-schema/pom.xml index c6e7d7d85b..e1d6526da9 100644 --- a/sonar/ooxml-schema/pom.xml +++ b/sonar/ooxml-schema/pom.xml @@ -6,7 +6,7 @@ org.apache.poi poi-parent - 3.15-beta3-SNAPSHOT + 3.15-beta4-SNAPSHOT .. poi-ooxml-schema diff --git a/sonar/ooxml/pom.xml b/sonar/ooxml/pom.xml index 3a0079cf61..305695ff1a 100644 --- a/sonar/ooxml/pom.xml +++ b/sonar/ooxml/pom.xml @@ -6,7 +6,7 @@ org.apache.poi poi-parent - 3.15-beta3-SNAPSHOT + 3.15-beta4-SNAPSHOT poi-ooxml jar diff --git a/sonar/pom.xml b/sonar/pom.xml index 508a02343e..5acb3a5da2 100644 --- a/sonar/pom.xml +++ b/sonar/pom.xml @@ -3,7 +3,7 @@ org.apache.poi poi-parent pom - 3.15-beta3-SNAPSHOT + 3.15-beta4-SNAPSHOT Apache POI - the Java API for Microsoft Documents Maven build of Apache POI for Sonar checks http://poi.apache.org/ diff --git a/sonar/scratchpad/pom.xml b/sonar/scratchpad/pom.xml index 790299cbe3..91ede69a1c 100644 --- a/sonar/scratchpad/pom.xml +++ b/sonar/scratchpad/pom.xml @@ -6,7 +6,7 @@ org.apache.poi poi-parent - 3.15-beta3-SNAPSHOT + 3.15-beta4-SNAPSHOT poi-scratchpad jar diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java index edd17dec55..38c298275b 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java @@ -1,1071 +1,1079 @@ -/* ==================================================================== - 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 java.awt.Color; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.apache.poi.sl.draw.DrawPaint; -import org.apache.poi.sl.usermodel.AutoNumberingScheme; -import org.apache.poi.sl.usermodel.PaintStyle; -import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; -import org.apache.poi.sl.usermodel.TextParagraph; -import org.apache.poi.util.Beta; -import org.apache.poi.util.Internal; -import org.apache.poi.util.Units; -import org.apache.poi.xslf.model.ParagraphPropertyFetcher; -import org.apache.xmlbeans.XmlCursor; -import org.apache.xmlbeans.XmlObject; -import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; -import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; -import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextAutonumberBullet; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePercent; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePoint; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharBullet; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStop; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStopList; -import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType; -import org.openxmlformats.schemas.drawingml.x2006.main.STTextAutonumberScheme; -import org.openxmlformats.schemas.drawingml.x2006.main.STTextFontAlignType; -import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; -import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; - -/** - * Represents a paragraph of text within the containing text body. - * The paragraph is the highest level text separation mechanism. - * - * @since POI-3.8 - */ -@Beta -public class XSLFTextParagraph implements TextParagraph { - private final CTTextParagraph _p; - private final List _runs; - private final XSLFTextShape _shape; - - XSLFTextParagraph(CTTextParagraph p, XSLFTextShape shape){ - _p = p; - _runs = new ArrayList(); - _shape = shape; - - for(XmlObject ch : _p.selectPath("*")){ - if(ch instanceof CTRegularTextRun){ - CTRegularTextRun r = (CTRegularTextRun)ch; - _runs.add(newTextRun(r)); - } else if (ch instanceof CTTextLineBreak){ - CTTextLineBreak br = (CTTextLineBreak)ch; - CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); - r.setRPr(br.getRPr()); - r.setT("\n"); - _runs.add(newTextRun(r)); - } else if (ch instanceof CTTextField){ - CTTextField f = (CTTextField)ch; - CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); - r.setRPr(f.getRPr()); - r.setT(f.getT()); - _runs.add(newTextRun(r)); - } - } - } - - public String getText(){ - StringBuilder out = new StringBuilder(); - for (XSLFTextRun r : _runs) { - out.append(r.getRawText()); - } - return out.toString(); - } - - String getRenderableText(){ - StringBuilder out = new StringBuilder(); - for (XSLFTextRun r : _runs) { - out.append(r.getRenderableText()); - } - return out.toString(); - } - - @Internal - public CTTextParagraph getXmlObject(){ - return _p; - } - - public XSLFTextShape getParentShape() { - return _shape; - - } - - @Override - public List getTextRuns(){ - return _runs; - } - - public Iterator iterator(){ - return _runs.iterator(); - } - - /** - * Add a new run of text - * - * @return a new run of text - */ - public XSLFTextRun addNewTextRun(){ - CTRegularTextRun r = _p.addNewR(); - CTTextCharacterProperties rPr = r.addNewRPr(); - rPr.setLang("en-US"); - XSLFTextRun run = newTextRun(r); - _runs.add(run); - return run; - } - - /** - * Insert a line break - * - * @return text run representing this line break ('\n') - */ - public XSLFTextRun 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(true); - brProps.set(prevRun); - } - CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); - r.setRPr(brProps); - r.setT("\n"); - XSLFTextRun run = new XSLFLineBreak(r, this, brProps); - _runs.add(run); - return run; - } - - @Override - public TextAlign getTextAlign(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetAlgn()){ - TextAlign val = TextAlign.values()[props.getAlgn().intValue() - 1]; - setValue(val); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue(); - } - - @Override - 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)); - } - } - - @Override - public FontAlign getFontAlign(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetFontAlgn()){ - FontAlign val = FontAlign.values()[props.getFontAlgn().intValue() - 1]; - setValue(val); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue(); - } - - /** - * Specifies the font alignment that is to be applied to the paragraph. - * Possible values for this include auto, top, center, baseline and bottom. - * see {@link org.apache.poi.sl.usermodel.TextParagraph.FontAlign}. - * - * @param align font align - */ - public void setFontAlign(FontAlign 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)); - } - } - - - - /** - * @return the font to be used on bullet characters within a given paragraph - */ - public String getBulletFont(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetBuFont()){ - setValue(props.getBuFont().getTypeface()); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue(); - } - - public void setBulletFont(String typeface){ - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - CTTextFont font = pr.isSetBuFont() ? pr.getBuFont() : pr.addNewBuFont(); - font.setTypeface(typeface); - } - - /** - * @return the character to be used in place of the standard bullet point - */ - public String getBulletCharacter(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetBuChar()){ - setValue(props.getBuChar().getChar()); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue(); - } - - public void setBulletCharacter(String str){ - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - CTTextCharBullet c = pr.isSetBuChar() ? pr.getBuChar() : pr.addNewBuChar(); - c.setChar(str); - } - - /** - * - * @return the color of bullet characters within a given paragraph. - * A null value means to use the text font color. - */ - public PaintStyle getBulletFontColor(){ - final XSLFTheme theme = getParentShape().getSheet().getTheme(); - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetBuClr()){ - XSLFColor c = new XSLFColor(props.getBuClr(), theme, null); - setValue(c.getColor()); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - Color col = fetcher.getValue(); - return (col == null) ? null : DrawPaint.createSolidPaint(col); - } - - public void setBulletFontColor(Color color) { - setBulletFontColor(DrawPaint.createSolidPaint(color)); - } - - - /** - * Set the color to be used on bullet characters within a given paragraph. - * - * @param color the bullet color - */ - public void setBulletFontColor(PaintStyle color) { - if (!(color instanceof SolidPaint)) { - throw new IllegalArgumentException("Currently XSLF only supports SolidPaint"); - } - - // TODO: implement setting bullet color to null - SolidPaint sp = (SolidPaint)color; - Color col = DrawPaint.applyColorTransform(sp.getSolidColor()); - - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - CTColor c = pr.isSetBuClr() ? pr.getBuClr() : pr.addNewBuClr(); - CTSRgbColor clr = c.isSetSrgbClr() ? c.getSrgbClr() : c.addNewSrgbClr(); - clr.setVal(new byte[]{(byte) col.getRed(), (byte) col.getGreen(), (byte) col.getBlue()}); - } - - /** - * Returns the bullet size that is to be used within a paragraph. - * This may be specified in two different ways, percentage spacing and font point spacing: - *

- * If bulletSize >= 0, then bulletSize is a percentage of the font size. - * If bulletSize < 0, then it specifies the size in points - *

- * - * @return the bullet size - */ - public Double getBulletFontSize(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetBuSzPct()){ - setValue(props.getBuSzPct().getVal() * 0.001); - return true; - } - if(props.isSetBuSzPts()){ - setValue( - props.getBuSzPts().getVal() * 0.01); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue(); - } - - /** - * Sets the bullet size that is to be used within a paragraph. - * This may be specified in two different ways, percentage spacing and font point spacing: - *

- * If bulletSize >= 0, then bulletSize is a percentage of the font size. - * If bulletSize < 0, then it specifies the size in points - *

- */ - public void setBulletFontSize(double bulletSize){ - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - - if(bulletSize >= 0) { - CTTextBulletSizePercent pt = pr.isSetBuSzPct() ? pr.getBuSzPct() : pr.addNewBuSzPct(); - pt.setVal((int)(bulletSize*1000)); - if(pr.isSetBuSzPts()) pr.unsetBuSzPts(); - } else { - CTTextBulletSizePoint pt = pr.isSetBuSzPts() ? pr.getBuSzPts() : pr.addNewBuSzPts(); - pt.setVal((int)(-bulletSize*100)); - if(pr.isSetBuSzPct()) pr.unsetBuSzPct(); - } - } - - /** - * @return the auto numbering scheme, or null if not defined - */ - public AutoNumberingScheme getAutoNumberingScheme() { - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()) { - public boolean fetch(CTTextParagraphProperties props) { - if (props.isSetBuAutoNum()) { - AutoNumberingScheme ans = AutoNumberingScheme.forOoxmlID(props.getBuAutoNum().getType().intValue()); - if (ans != null) { - setValue(ans); - return true; - } - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue(); - } - - /** - * @return the auto numbering starting number, or null if not defined - */ - public Integer getAutoNumberingStartAt() { - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()) { - public boolean fetch(CTTextParagraphProperties props) { - if (props.isSetBuAutoNum()) { - if (props.getBuAutoNum().isSetStartAt()) { - setValue(props.getBuAutoNum().getStartAt()); - return true; - } - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue(); - } - - - @Override - public void setIndent(Double indent){ - if ((indent == null) && !_p.isSetPPr()) return; - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - if(indent == null) { - if(pr.isSetIndent()) pr.unsetIndent(); - } else { - pr.setIndent(Units.toEMU(indent)); - } - } - - @Override - public Double getIndent() { - - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetIndent()){ - setValue(Units.toPoints(props.getIndent())); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - - return fetcher.getValue(); - } - - @Override - public void setLeftMargin(Double leftMargin){ - if (leftMargin == null && !_p.isSetPPr()) return; - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - if (leftMargin == null) { - if(pr.isSetMarL()) pr.unsetMarL(); - } else { - pr.setMarL(Units.toEMU(leftMargin)); - } - - } - - /** - * @return the left margin (in points) of the paragraph, null if unset - */ - @Override - public Double getLeftMargin(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetMarL()){ - double val = Units.toPoints(props.getMarL()); - setValue(val); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - // if the marL attribute is omitted, then a value of 347663 is implied - return fetcher.getValue(); - } - - @Override - public void setRightMargin(Double rightMargin){ - if (rightMargin == null && !_p.isSetPPr()) return; - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - if(rightMargin == null) { - if(pr.isSetMarR()) pr.unsetMarR(); - } else { - pr.setMarR(Units.toEMU(rightMargin)); - } - } - - /** - * - * @return the right margin of the paragraph, null if unset - */ - @Override - public Double getRightMargin(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetMarR()){ - double val = Units.toPoints(props.getMarR()); - setValue(val); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue(); - } - - @Override - public Double getDefaultTabSize(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetDefTabSz()){ - double val = Units.toPoints(props.getDefTabSz()); - setValue(val); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue(); - } - - public double getTabStop(final int idx){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetTabLst()){ - CTTextTabStopList tabStops = props.getTabLst(); - if(idx < tabStops.sizeOfTabArray() ) { - CTTextTabStop ts = tabStops.getTabArray(idx); - double val = Units.toPoints(ts.getPos()); - setValue(val); - return true; - } - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue() == null ? 0. : fetcher.getValue(); - } - - 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)); - } - - @Override - public void setLineSpacing(Double lineSpacing){ - if (lineSpacing == null && !_p.isSetPPr()) return; - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - if(lineSpacing == null) { - if (pr.isSetLnSpc()) pr.unsetLnSpc(); - } else { - CTTextSpacing spc = (pr.isSetLnSpc()) ? pr.getLnSpc() : pr.addNewLnSpc(); - if (lineSpacing >= 0) { - (spc.isSetSpcPct() ? spc.getSpcPct() : spc.addNewSpcPct()).setVal((int)(lineSpacing*1000)); - if (spc.isSetSpcPts()) spc.unsetSpcPts(); - } else { - (spc.isSetSpcPts() ? spc.getSpcPts() : spc.addNewSpcPts()).setVal((int)(-lineSpacing*100)); - if (spc.isSetSpcPct()) spc.unsetSpcPct(); - } - } - } - - @Override - public Double getLineSpacing(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetLnSpc()){ - CTTextSpacing spc = props.getLnSpc(); - - if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); - else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - - Double lnSpc = fetcher.getValue(); - if (lnSpc != null && lnSpc > 0) { - // check if the percentage value is scaled - CTTextNormalAutofit normAutofit = getParentShape().getTextBodyPr().getNormAutofit(); - if(normAutofit != null) { - double scale = 1 - (double)normAutofit.getLnSpcReduction() / 100000; - lnSpc *= scale; - } - } - - return lnSpc; - } - - @Override - public void setSpaceBefore(Double spaceBefore){ - if (spaceBefore == null && !_p.isSetPPr()) { - return; - } - - // unset the space before on null input - if (spaceBefore == null) { - if(_p.getPPr().isSetSpcBef()) { - _p.getPPr().unsetSpcBef(); - } - return; - } - - 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); - } - - @Override - public Double getSpaceBefore(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetSpcBef()){ - CTTextSpacing spc = props.getSpcBef(); - - if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); - else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - - return fetcher.getValue(); - } - - @Override - public void setSpaceAfter(Double spaceAfter){ - if (spaceAfter == null && !_p.isSetPPr()) { - return; - } - - // unset the space before on null input - if (spaceAfter == null) { - if(_p.getPPr().isSetSpcAft()) { - _p.getPPr().unsetSpcAft(); - } - return; - } - - 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); - } - - @Override - public Double getSpaceAfter(){ - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetSpcAft()){ - CTTextSpacing spc = props.getSpcAft(); - - if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); - else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue(); - } - - @Override - public void setIndentLevel(int level){ - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - pr.setLvl(level); - } - - @Override - public int getIndentLevel() { - CTTextParagraphProperties pr = _p.getPPr(); - return (pr == null || !pr.isSetLvl()) ? 0 : pr.getLvl(); - } - - /** - * Returns whether this paragraph has bullets - */ - public boolean isBullet() { - ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ - public boolean fetch(CTTextParagraphProperties props){ - if(props.isSetBuNone()) { - setValue(false); - return true; - } - if(props.isSetBuFont() || props.isSetBuChar()){ - setValue(true); - return true; - } - return false; - } - }; - fetchParagraphProperty(fetcher); - return fetcher.getValue() == null ? false : fetcher.getValue(); - } - - /** - * - * @param flag whether text in this paragraph has bullets - */ - public void setBullet(boolean flag) { - if(isBullet() == flag) return; - - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - if(flag) { - pr.addNewBuFont().setTypeface("Arial"); - pr.addNewBuChar().setChar("\u2022"); - } else { - if (pr.isSetBuFont()) pr.unsetBuFont(); - if (pr.isSetBuChar()) pr.unsetBuChar(); - if (pr.isSetBuAutoNum()) pr.unsetBuAutoNum(); - if (pr.isSetBuBlip()) pr.unsetBuBlip(); - if (pr.isSetBuClr()) pr.unsetBuClr(); - if (pr.isSetBuClrTx()) pr.unsetBuClrTx(); - if (pr.isSetBuFont()) pr.unsetBuFont(); - if (pr.isSetBuFontTx()) pr.unsetBuFontTx(); - if (pr.isSetBuSzPct()) pr.unsetBuSzPct(); - if (pr.isSetBuSzPts()) pr.unsetBuSzPts(); - if (pr.isSetBuSzTx()) pr.unsetBuSzTx(); - pr.addNewBuNone(); - } - } - - /** - * Specifies that automatic numbered bullet points should be applied to this paragraph - * - * @param scheme type of auto-numbering - * @param startAt the number that will start number for a given sequence of automatically - numbered bullets (1-based). - */ - public void setBulletAutoNumber(AutoNumberingScheme scheme, int startAt) { - if(startAt < 1) throw new IllegalArgumentException("Start Number must be greater or equal that 1") ; - CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); - CTTextAutonumberBullet lst = pr.isSetBuAutoNum() ? pr.getBuAutoNum() : pr.addNewBuAutoNum(); - lst.setType(STTextAutonumberScheme.Enum.forInt(scheme.ooxmlId)); - lst.setStartAt(startAt); - } - - @Override - public String toString(){ - return "[" + getClass() + "]" + getText(); - } - - - /* package */ CTTextParagraphProperties getDefaultMasterStyle(){ - CTPlaceholder ph = _shape.getCTPlaceholder(); - String defaultStyleSelector; - switch(ph == null ? -1 : ph.getType().intValue()) { - case STPlaceholderType.INT_TITLE: - case STPlaceholderType.INT_CTR_TITLE: - defaultStyleSelector = "titleStyle"; - break; - case -1: // no placeholder means plain text box - case STPlaceholderType.INT_FTR: - case STPlaceholderType.INT_SLD_NUM: - case STPlaceholderType.INT_DT: - defaultStyleSelector = "otherStyle"; - break; - default: - defaultStyleSelector = "bodyStyle"; - break; - } - int level = getIndentLevel(); - - // wind up and find the root master sheet which must be slide master - final String nsPML = "http://schemas.openxmlformats.org/presentationml/2006/main"; - final String nsDML = "http://schemas.openxmlformats.org/drawingml/2006/main"; - XSLFSheet masterSheet = _shape.getSheet(); - for (XSLFSheet m = masterSheet; m != null; m = (XSLFSheet)m.getMasterSheet()) { - masterSheet = m; - XmlObject xo = masterSheet.getXmlObject(); - XmlCursor cur = xo.newCursor(); - try { - cur.push(); - if ((cur.toChild(nsPML, "txStyles") && cur.toChild(nsPML, defaultStyleSelector)) || - (cur.pop() && cur.toChild(nsPML, "notesStyle"))) { - while (level >= 0) { - cur.push(); - if (cur.toChild(nsDML, "lvl" +(level+1)+ "pPr")) { - return (CTTextParagraphProperties)cur.getObject(); - } - cur.pop(); - level--; - } - } - } finally { - cur.dispose(); - } - } - - return null; - } - - private boolean fetchParagraphProperty(ParagraphPropertyFetcher visitor){ - boolean ok = false; - XSLFTextShape shape = getParentShape(); - XSLFSheet sheet = shape.getSheet(); - - if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr()); - if (ok) return true; - - ok = shape.fetchShapeProperty(visitor); - if (ok) return true; - - - CTPlaceholder ph = shape.getCTPlaceholder(); - if(ph == null){ - // if it is a plain text box then take defaults from presentation.xml - @SuppressWarnings("resource") - XMLSlideShow ppt = sheet.getSlideShow(); - CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getIndentLevel()); - if (themeProps != null) ok = visitor.fetch(themeProps); - } - if (ok) return true; - - // defaults for placeholders are defined in the slide master - CTTextParagraphProperties defaultProps = getDefaultMasterStyle(); - // TODO: determine master shape - if(defaultProps != null) ok = visitor.fetch(defaultProps); - if (ok) return true; - - return false; - } - - void copy(XSLFTextParagraph other){ - if (other == this) return; - - CTTextParagraph thisP = getXmlObject(); - CTTextParagraph otherP = other.getXmlObject(); - - if (thisP.isSetPPr()) thisP.unsetPPr(); - if (thisP.isSetEndParaRPr()) thisP.unsetEndParaRPr(); - - _runs.clear(); - for (int i=thisP.sizeOfBrArray(); i>0; i--) { - thisP.removeBr(i-1); - } - for (int i=thisP.sizeOfRArray(); i>0; i--) { - thisP.removeR(i-1); - } - for (int i=thisP.sizeOfFldArray(); i>0; i--) { - thisP.removeFld(i-1); - } - - XmlCursor thisC = thisP.newCursor(); - thisC.toEndToken(); - XmlCursor otherC = otherP.newCursor(); - otherC.copyXmlContents(thisC); - otherC.dispose(); - thisC.dispose(); - - List otherRs = other.getTextRuns(); - int i=0; - for(CTRegularTextRun rtr : thisP.getRArray()) { - XSLFTextRun run = newTextRun(rtr); - run.copy(otherRs.get(i++)); - _runs.add(run); - } - - - // set properties again, in case we are based on a different - // template - TextAlign srcAlign = other.getTextAlign(); - if(srcAlign != getTextAlign()){ - setTextAlign(srcAlign); - } - - boolean isBullet = other.isBullet(); - if(isBullet != isBullet()){ - setBullet(isBullet); - if(isBullet) { - String buFont = other.getBulletFont(); - if(buFont != null && !buFont.equals(getBulletFont())){ - setBulletFont(buFont); - } - String buChar = other.getBulletCharacter(); - if(buChar != null && !buChar.equals(getBulletCharacter())){ - setBulletCharacter(buChar); - } - PaintStyle buColor = other.getBulletFontColor(); - if(buColor != null && !buColor.equals(getBulletFontColor())){ - setBulletFontColor(buColor); - } - Double buSize = other.getBulletFontSize(); - if(!doubleEquals(buSize, getBulletFontSize())){ - setBulletFontSize(buSize); - } - } - } - - Double leftMargin = other.getLeftMargin(); - if (!doubleEquals(leftMargin, getLeftMargin())){ - setLeftMargin(leftMargin); - } - - Double indent = other.getIndent(); - if (!doubleEquals(indent, getIndent())) { - setIndent(indent); - } - - Double spaceAfter = other.getSpaceAfter(); - if (!doubleEquals(spaceAfter, getSpaceAfter())) { - setSpaceAfter(spaceAfter); - } - - Double spaceBefore = other.getSpaceBefore(); - if (!doubleEquals(spaceBefore, getSpaceBefore())) { - setSpaceBefore(spaceBefore); - } - - Double lineSpacing = other.getLineSpacing(); - if (!doubleEquals(lineSpacing, getLineSpacing())) { - setLineSpacing(lineSpacing); - } - } - - private static boolean doubleEquals(Double d1, Double d2) { - return (d1 == d2 || (d1 != null && d1.equals(d2))); - } - - @Override - public Double getDefaultFontSize() { - CTTextCharacterProperties endPr = _p.getEndParaRPr(); - if (endPr == null || !endPr.isSetSz()) { - endPr = getDefaultMasterStyle().getDefRPr(); - } - return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100.); - } - - @Override - public String getDefaultFontFamily() { - return (_runs.isEmpty() ? "Arial" : _runs.get(0).getFontFamily()); - } - - @Override - public BulletStyle getBulletStyle() { - if (!isBullet()) return null; - return new BulletStyle(){ - @Override - public String getBulletCharacter() { - return XSLFTextParagraph.this.getBulletCharacter(); - } - - @Override - public String getBulletFont() { - return XSLFTextParagraph.this.getBulletFont(); - } - - @Override - public Double getBulletFontSize() { - return XSLFTextParagraph.this.getBulletFontSize(); - } - - @Override - public PaintStyle getBulletFontColor() { - return XSLFTextParagraph.this.getBulletFontColor(); - } - - @Override - public void setBulletFontColor(Color color) { - setBulletFontColor(DrawPaint.createSolidPaint(color)); - } - - @Override - public void setBulletFontColor(PaintStyle color) { - XSLFTextParagraph.this.setBulletFontColor(color); - } - - @Override - public AutoNumberingScheme getAutoNumberingScheme() { - return XSLFTextParagraph.this.getAutoNumberingScheme(); - } - - @Override - public Integer getAutoNumberingStartAt() { - return XSLFTextParagraph.this.getAutoNumberingStartAt(); - } - - }; - } - - @Override - public void setBulletStyle(Object... styles) { - if (styles.length == 0) { - setBullet(false); - } else { - setBullet(true); - for (Object ostyle : styles) { - if (ostyle instanceof Number) { - setBulletFontSize(((Number)ostyle).doubleValue()); - } else if (ostyle instanceof Color) { - setBulletFontColor((Color)ostyle); - } else if (ostyle instanceof Character) { - setBulletCharacter(ostyle.toString()); - } else if (ostyle instanceof String) { - setBulletFont((String)ostyle); - } else if (ostyle instanceof AutoNumberingScheme) { - setBulletAutoNumber((AutoNumberingScheme)ostyle, 0); - } - } - } - } - - /** - * Helper method for appending text and keeping paragraph and character properties. - * The character properties are moved to the end paragraph marker - */ - /* package */ void clearButKeepProperties() { - CTTextParagraph thisP = getXmlObject(); - for (int i=thisP.sizeOfBrArray(); i>0; i--) { - thisP.removeBr(i-1); - } - for (int i=thisP.sizeOfFldArray(); i>0; i--) { - thisP.removeFld(i-1); - } - if (!_runs.isEmpty()) { - int size = _runs.size(); - XSLFTextRun lastRun = _runs.get(size-1); - CTTextCharacterProperties cpOther = lastRun.getRPr(false); - if (cpOther != null) { - if (thisP.isSetEndParaRPr()) { - thisP.unsetEndParaRPr(); - } - CTTextCharacterProperties cp = thisP.addNewEndParaRPr(); - cp.set(cpOther); - } - for (int i=size; i>0; i--) { - thisP.removeR(i-1); - } - _runs.clear(); - } - } - - @Override - public boolean isHeaderOrFooter() { - CTPlaceholder ph = _shape.getCTPlaceholder(); - int phId = (ph == null ? -1 : ph.getType().intValue()); - switch (phId) { - case STPlaceholderType.INT_SLD_NUM: - case STPlaceholderType.INT_DT: - case STPlaceholderType.INT_FTR: - case STPlaceholderType.INT_HDR: - return true; - default: - return false; - } - } - - /** - * Helper method to allow subclasses to provide their own text run - * - * @param r the xml reference - * - * @return a new text paragraph - * - * @since POI 3.15-beta2 - */ - protected XSLFTextRun newTextRun(CTRegularTextRun r) { - return new XSLFTextRun(r, this); - } -} \ No newline at end of file +/* ==================================================================== + 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 java.awt.Color; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.poi.sl.draw.DrawPaint; +import org.apache.poi.sl.usermodel.AutoNumberingScheme; +import org.apache.poi.sl.usermodel.PaintStyle; +import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; +import org.apache.poi.sl.usermodel.TextParagraph; +import org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.apache.poi.util.Units; +import org.apache.poi.xslf.model.ParagraphPropertyFetcher; +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlObject; +import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; +import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextAutonumberBullet; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePercent; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePoint; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharBullet; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStop; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStopList; +import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType; +import org.openxmlformats.schemas.drawingml.x2006.main.STTextAutonumberScheme; +import org.openxmlformats.schemas.drawingml.x2006.main.STTextFontAlignType; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; +import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; + +/** + * Represents a paragraph of text within the containing text body. + * The paragraph is the highest level text separation mechanism. + * + * @since POI-3.8 + */ +@Beta +public class XSLFTextParagraph implements TextParagraph { + private final CTTextParagraph _p; + private final List _runs; + private final XSLFTextShape _shape; + + XSLFTextParagraph(CTTextParagraph p, XSLFTextShape shape){ + _p = p; + _runs = new ArrayList(); + _shape = shape; + + for(XmlObject ch : _p.selectPath("*")){ + if(ch instanceof CTRegularTextRun){ + CTRegularTextRun r = (CTRegularTextRun)ch; + _runs.add(newTextRun(r)); + } else if (ch instanceof CTTextLineBreak){ + CTTextLineBreak br = (CTTextLineBreak)ch; + CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); + r.setRPr(br.getRPr()); + r.setT("\n"); + _runs.add(newTextRun(r)); + } else if (ch instanceof CTTextField){ + CTTextField f = (CTTextField)ch; + CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); + r.setRPr(f.getRPr()); + r.setT(f.getT()); + _runs.add(newTextRun(r)); + } + } + } + + public String getText(){ + StringBuilder out = new StringBuilder(); + for (XSLFTextRun r : _runs) { + out.append(r.getRawText()); + } + return out.toString(); + } + + String getRenderableText(){ + StringBuilder out = new StringBuilder(); + for (XSLFTextRun r : _runs) { + out.append(r.getRenderableText()); + } + return out.toString(); + } + + @Internal + public CTTextParagraph getXmlObject(){ + return _p; + } + + public XSLFTextShape getParentShape() { + return _shape; + + } + + @Override + public List getTextRuns(){ + return _runs; + } + + public Iterator iterator(){ + return _runs.iterator(); + } + + /** + * Add a new run of text + * + * @return a new run of text + */ + public XSLFTextRun addNewTextRun(){ + CTRegularTextRun r = _p.addNewR(); + CTTextCharacterProperties rPr = r.addNewRPr(); + rPr.setLang("en-US"); + XSLFTextRun run = newTextRun(r); + _runs.add(run); + return run; + } + + /** + * Insert a line break + * + * @return text run representing this line break ('\n') + */ + public XSLFTextRun 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(true); + brProps.set(prevRun); + } + CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); + r.setRPr(brProps); + r.setT("\n"); + XSLFTextRun run = new XSLFLineBreak(r, this, brProps); + _runs.add(run); + return run; + } + + @Override + public TextAlign getTextAlign(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetAlgn()){ + TextAlign val = TextAlign.values()[props.getAlgn().intValue() - 1]; + setValue(val); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + @Override + 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)); + } + } + + @Override + public FontAlign getFontAlign(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetFontAlgn()){ + FontAlign val = FontAlign.values()[props.getFontAlgn().intValue() - 1]; + setValue(val); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + /** + * Specifies the font alignment that is to be applied to the paragraph. + * Possible values for this include auto, top, center, baseline and bottom. + * see {@link org.apache.poi.sl.usermodel.TextParagraph.FontAlign}. + * + * @param align font align + */ + public void setFontAlign(FontAlign 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)); + } + } + + + + /** + * @return the font to be used on bullet characters within a given paragraph + */ + public String getBulletFont(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuFont()){ + setValue(props.getBuFont().getTypeface()); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + public void setBulletFont(String typeface){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextFont font = pr.isSetBuFont() ? pr.getBuFont() : pr.addNewBuFont(); + font.setTypeface(typeface); + } + + /** + * @return the character to be used in place of the standard bullet point + */ + public String getBulletCharacter(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuChar()){ + setValue(props.getBuChar().getChar()); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + public void setBulletCharacter(String str){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextCharBullet c = pr.isSetBuChar() ? pr.getBuChar() : pr.addNewBuChar(); + c.setChar(str); + } + + /** + * + * @return the color of bullet characters within a given paragraph. + * A null value means to use the text font color. + */ + public PaintStyle getBulletFontColor(){ + final XSLFTheme theme = getParentShape().getSheet().getTheme(); + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuClr()){ + XSLFColor c = new XSLFColor(props.getBuClr(), theme, null); + setValue(c.getColor()); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + Color col = fetcher.getValue(); + return (col == null) ? null : DrawPaint.createSolidPaint(col); + } + + public void setBulletFontColor(Color color) { + setBulletFontColor(DrawPaint.createSolidPaint(color)); + } + + + /** + * Set the color to be used on bullet characters within a given paragraph. + * + * @param color the bullet color + */ + public void setBulletFontColor(PaintStyle color) { + if (!(color instanceof SolidPaint)) { + throw new IllegalArgumentException("Currently XSLF only supports SolidPaint"); + } + + // TODO: implement setting bullet color to null + SolidPaint sp = (SolidPaint)color; + Color col = DrawPaint.applyColorTransform(sp.getSolidColor()); + + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTColor c = pr.isSetBuClr() ? pr.getBuClr() : pr.addNewBuClr(); + CTSRgbColor clr = c.isSetSrgbClr() ? c.getSrgbClr() : c.addNewSrgbClr(); + clr.setVal(new byte[]{(byte) col.getRed(), (byte) col.getGreen(), (byte) col.getBlue()}); + } + + /** + * Returns the bullet size that is to be used within a paragraph. + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If bulletSize >= 0, then bulletSize is a percentage of the font size. + * If bulletSize < 0, then it specifies the size in points + *

+ * + * @return the bullet size + */ + public Double getBulletFontSize(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuSzPct()){ + setValue(props.getBuSzPct().getVal() * 0.001); + return true; + } + if(props.isSetBuSzPts()){ + setValue( - props.getBuSzPts().getVal() * 0.01); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + /** + * Sets the bullet size that is to be used within a paragraph. + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If bulletSize >= 0, then bulletSize is a percentage of the font size. + * If bulletSize < 0, then it specifies the size in points + *

+ */ + public void setBulletFontSize(double bulletSize){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + + if(bulletSize >= 0) { + CTTextBulletSizePercent pt = pr.isSetBuSzPct() ? pr.getBuSzPct() : pr.addNewBuSzPct(); + pt.setVal((int)(bulletSize*1000)); + if(pr.isSetBuSzPts()) pr.unsetBuSzPts(); + } else { + CTTextBulletSizePoint pt = pr.isSetBuSzPts() ? pr.getBuSzPts() : pr.addNewBuSzPts(); + pt.setVal((int)(-bulletSize*100)); + if(pr.isSetBuSzPct()) pr.unsetBuSzPct(); + } + } + + /** + * @return the auto numbering scheme, or null if not defined + */ + public AutoNumberingScheme getAutoNumberingScheme() { + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()) { + public boolean fetch(CTTextParagraphProperties props) { + if (props.isSetBuAutoNum()) { + AutoNumberingScheme ans = AutoNumberingScheme.forOoxmlID(props.getBuAutoNum().getType().intValue()); + if (ans != null) { + setValue(ans); + return true; + } + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + /** + * @return the auto numbering starting number, or null if not defined + */ + public Integer getAutoNumberingStartAt() { + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()) { + public boolean fetch(CTTextParagraphProperties props) { + if (props.isSetBuAutoNum()) { + if (props.getBuAutoNum().isSetStartAt()) { + setValue(props.getBuAutoNum().getStartAt()); + return true; + } + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + + @Override + public void setIndent(Double indent){ + if ((indent == null) && !_p.isSetPPr()) return; + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(indent == null) { + if(pr.isSetIndent()) pr.unsetIndent(); + } else { + pr.setIndent(Units.toEMU(indent)); + } + } + + @Override + public Double getIndent() { + + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetIndent()){ + setValue(Units.toPoints(props.getIndent())); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + + return fetcher.getValue(); + } + + @Override + public void setLeftMargin(Double leftMargin){ + if (leftMargin == null && !_p.isSetPPr()) return; + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if (leftMargin == null) { + if(pr.isSetMarL()) pr.unsetMarL(); + } else { + pr.setMarL(Units.toEMU(leftMargin)); + } + + } + + /** + * @return the left margin (in points) of the paragraph, null if unset + */ + @Override + public Double getLeftMargin(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetMarL()){ + double val = Units.toPoints(props.getMarL()); + setValue(val); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + // if the marL attribute is omitted, then a value of 347663 is implied + return fetcher.getValue(); + } + + @Override + public void setRightMargin(Double rightMargin){ + if (rightMargin == null && !_p.isSetPPr()) return; + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(rightMargin == null) { + if(pr.isSetMarR()) pr.unsetMarR(); + } else { + pr.setMarR(Units.toEMU(rightMargin)); + } + } + + /** + * + * @return the right margin of the paragraph, null if unset + */ + @Override + public Double getRightMargin(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetMarR()){ + double val = Units.toPoints(props.getMarR()); + setValue(val); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + @Override + public Double getDefaultTabSize(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetDefTabSz()){ + double val = Units.toPoints(props.getDefTabSz()); + setValue(val); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + public double getTabStop(final int idx){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetTabLst()){ + CTTextTabStopList tabStops = props.getTabLst(); + if(idx < tabStops.sizeOfTabArray() ) { + CTTextTabStop ts = tabStops.getTabArray(idx); + double val = Units.toPoints(ts.getPos()); + setValue(val); + return true; + } + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? 0. : fetcher.getValue(); + } + + 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)); + } + + @Override + public void setLineSpacing(Double lineSpacing){ + if (lineSpacing == null && !_p.isSetPPr()) return; + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(lineSpacing == null) { + if (pr.isSetLnSpc()) pr.unsetLnSpc(); + } else { + CTTextSpacing spc = (pr.isSetLnSpc()) ? pr.getLnSpc() : pr.addNewLnSpc(); + if (lineSpacing >= 0) { + (spc.isSetSpcPct() ? spc.getSpcPct() : spc.addNewSpcPct()).setVal((int)(lineSpacing*1000)); + if (spc.isSetSpcPts()) spc.unsetSpcPts(); + } else { + (spc.isSetSpcPts() ? spc.getSpcPts() : spc.addNewSpcPts()).setVal((int)(-lineSpacing*100)); + if (spc.isSetSpcPct()) spc.unsetSpcPct(); + } + } + } + + @Override + public Double getLineSpacing(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetLnSpc()){ + CTTextSpacing spc = props.getLnSpc(); + + if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); + else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + + Double lnSpc = fetcher.getValue(); + if (lnSpc != null && lnSpc > 0) { + // check if the percentage value is scaled + CTTextNormalAutofit normAutofit = getParentShape().getTextBodyPr().getNormAutofit(); + if(normAutofit != null) { + double scale = 1 - (double)normAutofit.getLnSpcReduction() / 100000; + lnSpc *= scale; + } + } + + return lnSpc; + } + + @Override + public void setSpaceBefore(Double spaceBefore){ + if (spaceBefore == null && !_p.isSetPPr()) { + return; + } + + // unset the space before on null input + if (spaceBefore == null) { + if(_p.getPPr().isSetSpcBef()) { + _p.getPPr().unsetSpcBef(); + } + return; + } + + 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); + } + + @Override + public Double getSpaceBefore(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetSpcBef()){ + CTTextSpacing spc = props.getSpcBef(); + + if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); + else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + + return fetcher.getValue(); + } + + @Override + public void setSpaceAfter(Double spaceAfter){ + if (spaceAfter == null && !_p.isSetPPr()) { + return; + } + + // unset the space before on null input + if (spaceAfter == null) { + if(_p.getPPr().isSetSpcAft()) { + _p.getPPr().unsetSpcAft(); + } + return; + } + + 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); + } + + @Override + public Double getSpaceAfter(){ + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetSpcAft()){ + CTTextSpacing spc = props.getSpcAft(); + + if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); + else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue(); + } + + @Override + public void setIndentLevel(int level){ + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + pr.setLvl(level); + } + + @Override + public int getIndentLevel() { + CTTextParagraphProperties pr = _p.getPPr(); + return (pr == null || !pr.isSetLvl()) ? 0 : pr.getLvl(); + } + + /** + * Returns whether this paragraph has bullets + */ + public boolean isBullet() { + ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getIndentLevel()){ + public boolean fetch(CTTextParagraphProperties props){ + if(props.isSetBuNone()) { + setValue(false); + return true; + } + if(props.isSetBuFont() || props.isSetBuChar()){ + setValue(true); + return true; + } + return false; + } + }; + fetchParagraphProperty(fetcher); + return fetcher.getValue() == null ? false : fetcher.getValue(); + } + + /** + * + * @param flag whether text in this paragraph has bullets + */ + public void setBullet(boolean flag) { + if(isBullet() == flag) return; + + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + if(flag) { + pr.addNewBuFont().setTypeface("Arial"); + pr.addNewBuChar().setChar("\u2022"); + } else { + if (pr.isSetBuFont()) pr.unsetBuFont(); + if (pr.isSetBuChar()) pr.unsetBuChar(); + if (pr.isSetBuAutoNum()) pr.unsetBuAutoNum(); + if (pr.isSetBuBlip()) pr.unsetBuBlip(); + if (pr.isSetBuClr()) pr.unsetBuClr(); + if (pr.isSetBuClrTx()) pr.unsetBuClrTx(); + if (pr.isSetBuFont()) pr.unsetBuFont(); + if (pr.isSetBuFontTx()) pr.unsetBuFontTx(); + if (pr.isSetBuSzPct()) pr.unsetBuSzPct(); + if (pr.isSetBuSzPts()) pr.unsetBuSzPts(); + if (pr.isSetBuSzTx()) pr.unsetBuSzTx(); + pr.addNewBuNone(); + } + } + + /** + * Specifies that automatic numbered bullet points should be applied to this paragraph + * + * @param scheme type of auto-numbering + * @param startAt the number that will start number for a given sequence of automatically + numbered bullets (1-based). + */ + public void setBulletAutoNumber(AutoNumberingScheme scheme, int startAt) { + if(startAt < 1) throw new IllegalArgumentException("Start Number must be greater or equal that 1") ; + CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); + CTTextAutonumberBullet lst = pr.isSetBuAutoNum() ? pr.getBuAutoNum() : pr.addNewBuAutoNum(); + lst.setType(STTextAutonumberScheme.Enum.forInt(scheme.ooxmlId)); + lst.setStartAt(startAt); + } + + @Override + public String toString(){ + return "[" + getClass() + "]" + getText(); + } + + + /** + * @return master style text paragraph properties, or null if + * there are no master slides or the master slides do not contain a text paragraph + */ + /* package */ CTTextParagraphProperties getDefaultMasterStyle(){ + CTPlaceholder ph = _shape.getCTPlaceholder(); + String defaultStyleSelector; + switch(ph == null ? -1 : ph.getType().intValue()) { + case STPlaceholderType.INT_TITLE: + case STPlaceholderType.INT_CTR_TITLE: + defaultStyleSelector = "titleStyle"; + break; + case -1: // no placeholder means plain text box + case STPlaceholderType.INT_FTR: + case STPlaceholderType.INT_SLD_NUM: + case STPlaceholderType.INT_DT: + defaultStyleSelector = "otherStyle"; + break; + default: + defaultStyleSelector = "bodyStyle"; + break; + } + int level = getIndentLevel(); + + // wind up and find the root master sheet which must be slide master + final String nsPML = "http://schemas.openxmlformats.org/presentationml/2006/main"; + final String nsDML = "http://schemas.openxmlformats.org/drawingml/2006/main"; + XSLFSheet masterSheet = _shape.getSheet(); + for (XSLFSheet m = masterSheet; m != null; m = (XSLFSheet)m.getMasterSheet()) { + masterSheet = m; + XmlObject xo = masterSheet.getXmlObject(); + XmlCursor cur = xo.newCursor(); + try { + cur.push(); + if ((cur.toChild(nsPML, "txStyles") && cur.toChild(nsPML, defaultStyleSelector)) || + (cur.pop() && cur.toChild(nsPML, "notesStyle"))) { + while (level >= 0) { + cur.push(); + if (cur.toChild(nsDML, "lvl" +(level+1)+ "pPr")) { + return (CTTextParagraphProperties)cur.getObject(); + } + cur.pop(); + level--; + } + } + } finally { + cur.dispose(); + } + } + + return null; + } + + private boolean fetchParagraphProperty(ParagraphPropertyFetcher visitor){ + boolean ok = false; + XSLFTextShape shape = getParentShape(); + XSLFSheet sheet = shape.getSheet(); + + if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr()); + if (ok) return true; + + ok = shape.fetchShapeProperty(visitor); + if (ok) return true; + + + CTPlaceholder ph = shape.getCTPlaceholder(); + if(ph == null){ + // if it is a plain text box then take defaults from presentation.xml + @SuppressWarnings("resource") + XMLSlideShow ppt = sheet.getSlideShow(); + CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getIndentLevel()); + if (themeProps != null) ok = visitor.fetch(themeProps); + } + if (ok) return true; + + // defaults for placeholders are defined in the slide master + CTTextParagraphProperties defaultProps = getDefaultMasterStyle(); + // TODO: determine master shape + if(defaultProps != null) ok = visitor.fetch(defaultProps); + if (ok) return true; + + return false; + } + + void copy(XSLFTextParagraph other){ + if (other == this) return; + + CTTextParagraph thisP = getXmlObject(); + CTTextParagraph otherP = other.getXmlObject(); + + if (thisP.isSetPPr()) thisP.unsetPPr(); + if (thisP.isSetEndParaRPr()) thisP.unsetEndParaRPr(); + + _runs.clear(); + for (int i=thisP.sizeOfBrArray(); i>0; i--) { + thisP.removeBr(i-1); + } + for (int i=thisP.sizeOfRArray(); i>0; i--) { + thisP.removeR(i-1); + } + for (int i=thisP.sizeOfFldArray(); i>0; i--) { + thisP.removeFld(i-1); + } + + XmlCursor thisC = thisP.newCursor(); + thisC.toEndToken(); + XmlCursor otherC = otherP.newCursor(); + otherC.copyXmlContents(thisC); + otherC.dispose(); + thisC.dispose(); + + List otherRs = other.getTextRuns(); + int i=0; + for(CTRegularTextRun rtr : thisP.getRArray()) { + XSLFTextRun run = newTextRun(rtr); + run.copy(otherRs.get(i++)); + _runs.add(run); + } + + + // set properties again, in case we are based on a different + // template + TextAlign srcAlign = other.getTextAlign(); + if(srcAlign != getTextAlign()){ + setTextAlign(srcAlign); + } + + boolean isBullet = other.isBullet(); + if(isBullet != isBullet()){ + setBullet(isBullet); + if(isBullet) { + String buFont = other.getBulletFont(); + if(buFont != null && !buFont.equals(getBulletFont())){ + setBulletFont(buFont); + } + String buChar = other.getBulletCharacter(); + if(buChar != null && !buChar.equals(getBulletCharacter())){ + setBulletCharacter(buChar); + } + PaintStyle buColor = other.getBulletFontColor(); + if(buColor != null && !buColor.equals(getBulletFontColor())){ + setBulletFontColor(buColor); + } + Double buSize = other.getBulletFontSize(); + if(!doubleEquals(buSize, getBulletFontSize())){ + setBulletFontSize(buSize); + } + } + } + + Double leftMargin = other.getLeftMargin(); + if (!doubleEquals(leftMargin, getLeftMargin())){ + setLeftMargin(leftMargin); + } + + Double indent = other.getIndent(); + if (!doubleEquals(indent, getIndent())) { + setIndent(indent); + } + + Double spaceAfter = other.getSpaceAfter(); + if (!doubleEquals(spaceAfter, getSpaceAfter())) { + setSpaceAfter(spaceAfter); + } + + Double spaceBefore = other.getSpaceBefore(); + if (!doubleEquals(spaceBefore, getSpaceBefore())) { + setSpaceBefore(spaceBefore); + } + + Double lineSpacing = other.getLineSpacing(); + if (!doubleEquals(lineSpacing, getLineSpacing())) { + setLineSpacing(lineSpacing); + } + } + + private static boolean doubleEquals(Double d1, Double d2) { + return (d1 == d2 || (d1 != null && d1.equals(d2))); + } + + @Override + public Double getDefaultFontSize() { + CTTextCharacterProperties endPr = _p.getEndParaRPr(); + if (endPr == null || !endPr.isSetSz()) { + // inherit the font size from the master style + CTTextParagraphProperties masterStyle = getDefaultMasterStyle(); + if (masterStyle != null) { + endPr = masterStyle.getDefRPr(); + } + } + return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100.); + } + + @Override + public String getDefaultFontFamily() { + return (_runs.isEmpty() ? "Arial" : _runs.get(0).getFontFamily()); + } + + @Override + public BulletStyle getBulletStyle() { + if (!isBullet()) return null; + return new BulletStyle(){ + @Override + public String getBulletCharacter() { + return XSLFTextParagraph.this.getBulletCharacter(); + } + + @Override + public String getBulletFont() { + return XSLFTextParagraph.this.getBulletFont(); + } + + @Override + public Double getBulletFontSize() { + return XSLFTextParagraph.this.getBulletFontSize(); + } + + @Override + public PaintStyle getBulletFontColor() { + return XSLFTextParagraph.this.getBulletFontColor(); + } + + @Override + public void setBulletFontColor(Color color) { + setBulletFontColor(DrawPaint.createSolidPaint(color)); + } + + @Override + public void setBulletFontColor(PaintStyle color) { + XSLFTextParagraph.this.setBulletFontColor(color); + } + + @Override + public AutoNumberingScheme getAutoNumberingScheme() { + return XSLFTextParagraph.this.getAutoNumberingScheme(); + } + + @Override + public Integer getAutoNumberingStartAt() { + return XSLFTextParagraph.this.getAutoNumberingStartAt(); + } + + }; + } + + @Override + public void setBulletStyle(Object... styles) { + if (styles.length == 0) { + setBullet(false); + } else { + setBullet(true); + for (Object ostyle : styles) { + if (ostyle instanceof Number) { + setBulletFontSize(((Number)ostyle).doubleValue()); + } else if (ostyle instanceof Color) { + setBulletFontColor((Color)ostyle); + } else if (ostyle instanceof Character) { + setBulletCharacter(ostyle.toString()); + } else if (ostyle instanceof String) { + setBulletFont((String)ostyle); + } else if (ostyle instanceof AutoNumberingScheme) { + setBulletAutoNumber((AutoNumberingScheme)ostyle, 0); + } + } + } + } + + /** + * Helper method for appending text and keeping paragraph and character properties. + * The character properties are moved to the end paragraph marker + */ + /* package */ void clearButKeepProperties() { + CTTextParagraph thisP = getXmlObject(); + for (int i=thisP.sizeOfBrArray(); i>0; i--) { + thisP.removeBr(i-1); + } + for (int i=thisP.sizeOfFldArray(); i>0; i--) { + thisP.removeFld(i-1); + } + if (!_runs.isEmpty()) { + int size = _runs.size(); + XSLFTextRun lastRun = _runs.get(size-1); + CTTextCharacterProperties cpOther = lastRun.getRPr(false); + if (cpOther != null) { + if (thisP.isSetEndParaRPr()) { + thisP.unsetEndParaRPr(); + } + CTTextCharacterProperties cp = thisP.addNewEndParaRPr(); + cp.set(cpOther); + } + for (int i=size; i>0; i--) { + thisP.removeR(i-1); + } + _runs.clear(); + } + } + + @Override + public boolean isHeaderOrFooter() { + CTPlaceholder ph = _shape.getCTPlaceholder(); + int phId = (ph == null ? -1 : ph.getType().intValue()); + switch (phId) { + case STPlaceholderType.INT_SLD_NUM: + case STPlaceholderType.INT_DT: + case STPlaceholderType.INT_FTR: + case STPlaceholderType.INT_HDR: + return true; + default: + return false; + } + } + + /** + * Helper method to allow subclasses to provide their own text run + * + * @param r the xml reference + * + * @return a new text paragraph + * + * @since POI 3.15-beta2 + */ + protected XSLFTextRun newTextRun(CTRegularTextRun r) { + return new XSLFTextRun(r, this); + } +} diff --git a/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFSheetAutoSizeColumn.java b/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFSheetAutoSizeColumn.java index 9685b950b8..15acbfca0b 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFSheetAutoSizeColumn.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFSheetAutoSizeColumn.java @@ -63,7 +63,7 @@ public class TestSXSSFSheetAutoSizeColumn { // longCellValue ends up with approx. column width 10_000 (on my machine) // so shortCellValue can be expected to be < 5000 for all fonts // and longCellValue can be expected to be > 5000 for all fonts - private static final int COLUMN_WIDTH_THRESHOLD_BETWEEN_SHORT_AND_LONG = 5000; + private static final int COLUMN_WIDTH_THRESHOLD_BETWEEN_SHORT_AND_LONG = 4000; private static final int MAX_COLUMN_WIDTH = 255*256; private static final SortedSet columns; diff --git a/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java b/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java index f248ab633d..44aa8a2d3e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java +++ b/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java @@ -32,9 +32,9 @@ import org.apache.poi.poifs.filesystem.*; * @author Nick Burch */ public final class PowerPointExtractor extends POIOLE2TextExtractor { - private HSLFSlideShowImpl _hslfshow; - private HSLFSlideShow _show; - private List _slides; + private final HSLFSlideShowImpl _hslfshow; + private final HSLFSlideShow _show; + private final List _slides; private boolean _slidesByDefault = true; private boolean _notesByDefault = false; @@ -240,9 +240,9 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { } // Slide header, if set - ret.append(headerText); + ret.append(headerText); - // Slide text + // Slide text textRunsToText(ret, slide.getTextParagraphs()); // Table text @@ -256,14 +256,13 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { // Comments, if requested and present if (getCommentText) { - Comment[] comments = slide.getComments(); - for (int j = 0; j < comments.length; j++) { - ret.append(comments[j].getAuthor() + " - " + comments[j].getText() + "\n"); + for (Comment comment : slide.getComments()) { + ret.append(comment.getAuthor() + " - " + comment.getText() + "\n"); } } } if (getNoteText) { - ret.append("\n"); + ret.append('\n'); } } @@ -271,7 +270,7 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { // Not currently using _notes, as that can have the notes of // master sheets in. Grab Slide list, then work from there, // but ensure no duplicates - HashSet seenNotes = new HashSet(); + Set seenNotes = new HashSet(); String headerText = ""; String footerText = ""; HeadersFooters hf = _show.getNotesHeadersFooters(); @@ -285,8 +284,8 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { } - for (int i = 0; i < _slides.size(); i++) { - HSLFNotes notes = _slides.get(i).getNotes(); + for (HSLFSlide slide : _slides) { + HSLFNotes notes = slide.getNotes(); if (notes == null) { continue; } @@ -297,13 +296,13 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { seenNotes.add(id); // Repeat the Notes header, if set - ret.append(headerText); + ret.append(headerText); // Notes text - textRunsToText(ret, notes.getTextParagraphs()); + textRunsToText(ret, notes.getTextParagraphs()); // Repeat the notes footer, if set - ret.append(footerText); + ret.append(footerText); } } @@ -315,16 +314,18 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { } private void extractTableText(StringBuffer ret, HSLFTable table) { - for (int row = 0; row < table.getNumberOfRows(); row++){ - for (int col = 0; col < table.getNumberOfColumns(); col++){ + final int nrows = table.getNumberOfRows(); + final int ncols = table.getNumberOfColumns(); + for (int row = 0; row < nrows; row++){ + for (int col = 0; col < ncols; col++){ HSLFTableCell cell = table.getCell(row, col); //defensive null checks; don't know if they're necessary if (cell != null){ String txt = cell.getText(); txt = (txt == null) ? "" : txt; ret.append(txt); - if (col < table.getNumberOfColumns()-1){ - ret.append("\t"); + if (col < ncols-1){ + ret.append('\t'); } } } @@ -339,7 +340,7 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { for (List lp : paragraphs) { ret.append(HSLFTextParagraph.getText(lp)); if (ret.length() > 0 && ret.charAt(ret.length()-1) != '\n') { - ret.append("\n"); + ret.append('\n'); } } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java b/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java index f34cd4be78..9bfa5f3254 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java @@ -36,6 +36,8 @@ import org.apache.poi.hslf.usermodel.HSLFTextShape; */ public final class HeadersFooters { + private static final String _ppt2007tag = "___PPT12"; + private final HeadersFootersContainer _container; private final HSLFSheet _sheet; private final boolean _ppt2007; @@ -54,7 +56,7 @@ public final class HeadersFooters { // detect if this ppt was saved in Office2007 String tag = ppt.getSlideMasters().get(0).getProgrammableTag(); - _ppt2007 = "___PPT12".equals(tag); + _ppt2007 = _ppt2007tag.equals(tag); SheetContainer sc = _sheet.getSheetContainer(); HeadersFootersContainer hdd = (HeadersFootersContainer)sc.findFirstOfType(RecordTypes.HeadersFooters.typeID); diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java index fe22c03eff..ce9d58f038 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java @@ -22,32 +22,10 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Iterator; import org.apache.poi.hpsf.DocumentSummaryInformation; import org.apache.poi.hpsf.SummaryInformation; -import org.apache.poi.hwpf.model.BookmarksTables; -import org.apache.poi.hwpf.model.CHPBinTable; -import org.apache.poi.hwpf.model.ComplexFileTable; -import org.apache.poi.hwpf.model.DocumentProperties; -import org.apache.poi.hwpf.model.EscherRecordHolder; -import org.apache.poi.hwpf.model.FSPADocumentPart; -import org.apache.poi.hwpf.model.FSPATable; -import org.apache.poi.hwpf.model.FieldsTables; -import org.apache.poi.hwpf.model.FontTable; -import org.apache.poi.hwpf.model.ListTables; -import org.apache.poi.hwpf.model.NoteType; -import org.apache.poi.hwpf.model.NotesTables; -import org.apache.poi.hwpf.model.PAPBinTable; -import org.apache.poi.hwpf.model.PicturesTable; -import org.apache.poi.hwpf.model.RevisionMarkAuthorTable; -import org.apache.poi.hwpf.model.SavedByTable; -import org.apache.poi.hwpf.model.SectionTable; -import org.apache.poi.hwpf.model.SinglentonTextPiece; -import org.apache.poi.hwpf.model.StyleSheet; -import org.apache.poi.hwpf.model.SubdocumentType; -import org.apache.poi.hwpf.model.TextPiece; -import org.apache.poi.hwpf.model.TextPieceTable; +import org.apache.poi.hwpf.model.*; import org.apache.poi.hwpf.model.io.HWPFFileSystem; import org.apache.poi.hwpf.model.io.HWPFOutputStream; import org.apache.poi.hwpf.usermodel.Bookmarks; @@ -572,12 +550,27 @@ public final class HWPFDocument extends HWPFDocumentCore { } /** - * Warning - not currently implemented for HWPF! + * Write out the word file that is represented by this class, to the + * currently open {@link File}, via the writeable {@link POIFSFileSystem} + * it was opened as. + * + *

This will fail (with an {@link IllegalStateException} if the + * Document was opened read-only, opened from an {@link InputStream} + * instead of a File, or if this is not the root document. For those cases, + * you must use {@link #write(OutputStream)} or {@link #write(File)} to + * write to a brand new document. + * + * @since 3.15 */ @Override public void write() throws IOException { - // TODO Implement - throw new IllegalStateException("Coming soon!"); + validateInPlaceWritePossible(); + + // Update the Document+Properties streams in the file + write(directory.getFileSystem(), false); + + // Sync with the File on disk + directory.getFileSystem().writeFilesystem(); } /** @@ -912,23 +905,18 @@ public final class HWPFDocument extends HWPFDocumentCore { dataBuf = tempBuf; } - // create new document preserving order of entries - // TODO Check "copyOtherEntries" and tweak behaviour based on that - // TODO That's needed for in-place write + // Create a new document preserving order of entries / Update existing boolean docWritten = false; boolean dataWritten = false; boolean objectPoolWritten = false; boolean tableWritten = false; boolean propertiesWritten = false; - for ( Iterator iter = directory.getEntries(); iter.hasNext(); ) - { - Entry entry = iter.next(); + for (Entry entry : directory) { if ( entry.getName().equals( STREAM_WORD_DOCUMENT ) ) { if ( !docWritten ) { - pfs.createDocument( new ByteArrayInputStream( mainBuf ), - STREAM_WORD_DOCUMENT ); + write(pfs, mainBuf, STREAM_WORD_DOCUMENT); docWritten = true; } } @@ -936,7 +924,11 @@ public final class HWPFDocument extends HWPFDocumentCore { { if ( !objectPoolWritten ) { - _objectPool.writeTo( pfs.getRoot() ); + if ( copyOtherEntries ) { + _objectPool.writeTo( pfs.getRoot() ); + } else { + // Object pool is already there, no need to change/copy + } objectPoolWritten = true; } } @@ -945,8 +937,7 @@ public final class HWPFDocument extends HWPFDocumentCore { { if ( !tableWritten ) { - pfs.createDocument( new ByteArrayInputStream( tableBuf ), - STREAM_TABLE_1 ); + write(pfs, tableBuf, STREAM_TABLE_1); tableWritten = true; } } @@ -965,29 +956,25 @@ public final class HWPFDocument extends HWPFDocumentCore { { if ( !dataWritten ) { - pfs.createDocument( new ByteArrayInputStream( dataBuf ), - STREAM_DATA ); + write(pfs, dataBuf, STREAM_DATA); dataWritten = true; } } - else + else if ( copyOtherEntries ) { EntryUtils.copyNodeRecursively( entry, pfs.getRoot() ); } } if ( !docWritten ) - pfs.createDocument( new ByteArrayInputStream( mainBuf ), - STREAM_WORD_DOCUMENT ); + write(pfs, mainBuf, STREAM_WORD_DOCUMENT); if ( !tableWritten ) - pfs.createDocument( new ByteArrayInputStream( tableBuf ), - STREAM_TABLE_1 ); + write(pfs, tableBuf, STREAM_TABLE_1); if ( !propertiesWritten ) writeProperties( pfs ); if ( !dataWritten ) - pfs.createDocument( new ByteArrayInputStream( dataBuf ), - STREAM_DATA ); - if ( !objectPoolWritten ) + write(pfs, dataBuf, STREAM_DATA); + if ( !objectPoolWritten && copyOtherEntries ) _objectPool.writeTo( pfs.getRoot() ); this.directory = pfs.getRoot(); @@ -1000,6 +987,9 @@ public final class HWPFDocument extends HWPFDocumentCore { this._tableStream = tableStream.toByteArray(); this._dataStream = dataBuf; } + private static void write(NPOIFSFileSystem pfs, byte[] data, String name) throws IOException { + pfs.createOrUpdateDocument(new ByteArrayInputStream(data), name); + } @Internal public byte[] getDataStream() diff --git a/src/scratchpad/src/org/apache/poi/hwpf/sprm/SectionSprmUncompressor.java b/src/scratchpad/src/org/apache/poi/hwpf/sprm/SectionSprmUncompressor.java index bc578720e5..0b186b56b6 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/sprm/SectionSprmUncompressor.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/sprm/SectionSprmUncompressor.java @@ -24,11 +24,12 @@ import org.apache.poi.util.Internal; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; + @Internal public final class SectionSprmUncompressor extends SprmUncompressor { private static final POILogger logger = POILogFactory.getLogger(SectionSprmUncompressor.class); - + public SectionSprmUncompressor() { } @@ -58,15 +59,14 @@ public final class SectionSprmUncompressor extends SprmUncompressor */ static void unCompressSEPOperation (SectionProperties newSEP, SprmOperation sprm) { - final int operation = sprm.getOperation(); - final int operand = sprm.getOperand(); + int operation = sprm.getOperation(); switch (operation) { case 0: - newSEP.setCnsPgn ((byte) operand); + newSEP.setCnsPgn ((byte) sprm.getOperand()); break; case 0x1: - newSEP.setIHeadingPgn ((byte) operand); + newSEP.setIHeadingPgn ((byte) sprm.getOperand()); break; case 0x2: byte[] buf = new byte[sprm.size() - 3]; @@ -80,110 +80,110 @@ public final class SectionSprmUncompressor extends SprmUncompressor //not quite sure break; case 0x5: - newSEP.setFEvenlySpaced (getFlag (operand)); + newSEP.setFEvenlySpaced (getFlag (sprm.getOperand())); break; case 0x6: - newSEP.setFUnlocked (getFlag (operand)); + newSEP.setFUnlocked (getFlag (sprm.getOperand())); break; case 0x7: - newSEP.setDmBinFirst ((short) operand); + newSEP.setDmBinFirst ((short) sprm.getOperand()); break; case 0x8: - newSEP.setDmBinOther ((short) operand); + newSEP.setDmBinOther ((short) sprm.getOperand()); break; case 0x9: - newSEP.setBkc ((byte) operand); + newSEP.setBkc ((byte) sprm.getOperand()); break; case 0xa: - newSEP.setFTitlePage (getFlag (operand)); + newSEP.setFTitlePage (getFlag (sprm.getOperand())); break; case 0xb: - newSEP.setCcolM1 ((short) operand); + newSEP.setCcolM1 ((short) sprm.getOperand()); break; case 0xc: - newSEP.setDxaColumns (operand); + newSEP.setDxaColumns (sprm.getOperand()); break; case 0xd: - newSEP.setFAutoPgn (getFlag (operand)); + newSEP.setFAutoPgn (getFlag (sprm.getOperand())); break; case 0xe: - newSEP.setNfcPgn ((byte) operand); + newSEP.setNfcPgn ((byte) sprm.getOperand()); break; case 0xf: - newSEP.setDyaPgn ((short) operand); + newSEP.setDyaPgn ((short) sprm.getOperand()); break; case 0x10: - newSEP.setDxaPgn ((short) operand); + newSEP.setDxaPgn ((short) sprm.getOperand()); break; case 0x11: - newSEP.setFPgnRestart (getFlag (operand)); + newSEP.setFPgnRestart (getFlag (sprm.getOperand())); break; case 0x12: - newSEP.setFEndNote (getFlag (operand)); + newSEP.setFEndNote (getFlag (sprm.getOperand())); break; case 0x13: - newSEP.setLnc ((byte) operand); + newSEP.setLnc ((byte) sprm.getOperand()); break; case 0x14: - newSEP.setGrpfIhdt ((byte) operand); + newSEP.setGrpfIhdt ((byte) sprm.getOperand()); break; case 0x15: - newSEP.setNLnnMod ((short) operand); + newSEP.setNLnnMod ((short) sprm.getOperand()); break; case 0x16: - newSEP.setDxaLnn (operand); + newSEP.setDxaLnn (sprm.getOperand()); break; case 0x17: - newSEP.setDyaHdrTop (operand); + newSEP.setDyaHdrTop (sprm.getOperand()); break; case 0x18: - newSEP.setDyaHdrBottom (operand); + newSEP.setDyaHdrBottom (sprm.getOperand()); break; case 0x19: - newSEP.setFLBetween (getFlag (operand)); + newSEP.setFLBetween (getFlag (sprm.getOperand())); break; case 0x1a: - newSEP.setVjc ((byte) operand); + newSEP.setVjc ((byte) sprm.getOperand()); break; case 0x1b: - newSEP.setLnnMin ((short) operand); + newSEP.setLnnMin ((short) sprm.getOperand()); break; case 0x1c: - newSEP.setPgnStart ((short) operand); + newSEP.setPgnStart ((short) sprm.getOperand()); break; case 0x1d: - newSEP.setDmOrientPage( operand != 0 ); + newSEP.setDmOrientPage( sprm.getOperand() != 0 ); break; case 0x1e: //nothing break; case 0x1f: - newSEP.setXaPage (operand); + newSEP.setXaPage (sprm.getOperand()); break; case 0x20: - newSEP.setYaPage (operand); + newSEP.setYaPage (sprm.getOperand()); break; case 0x21: - newSEP.setDxaLeft (operand); + newSEP.setDxaLeft (sprm.getOperand()); break; case 0x22: - newSEP.setDxaRight (operand); + newSEP.setDxaRight (sprm.getOperand()); break; case 0x23: - newSEP.setDyaTop (operand); + newSEP.setDyaTop (sprm.getOperand()); break; case 0x24: - newSEP.setDyaBottom (operand); + newSEP.setDyaBottom (sprm.getOperand()); break; case 0x25: - newSEP.setDzaGutter (operand); + newSEP.setDzaGutter (sprm.getOperand()); break; case 0x26: - newSEP.setDmPaperReq ((short) operand); + newSEP.setDmPaperReq ((short) sprm.getOperand()); break; case 0x27: - newSEP.setFPropMark (getFlag (operand)); + newSEP.setFPropMark (getFlag (sprm.getOperand())); break; case 0x28: break; @@ -204,40 +204,40 @@ public final class SectionSprmUncompressor extends SprmUncompressor newSEP.setBrcRight(new BorderCode(sprm.getGrpprl(), sprm.getGrpprlOffset())); break; case 0x2f: - newSEP.setPgbProp (operand); + newSEP.setPgbProp (sprm.getOperand()); break; case 0x30: - newSEP.setDxtCharSpace (operand); + newSEP.setDxtCharSpace (sprm.getOperand()); break; case 0x31: - newSEP.setDyaLinePitch (operand); + newSEP.setDyaLinePitch (sprm.getOperand()); break; case 0x33: - newSEP.setWTextFlow ((short) operand); + newSEP.setWTextFlow ((short) sprm.getOperand()); break; case 0x3C: // [MS-DOC], v20140721, 2.6.4, sprmSRncFtn - newSEP.setRncFtn((short) operand); + newSEP.setRncFtn((short) sprm.getOperand()); break; case 0x3E: // [MS-DOC], v20140721, 2.6.4, sprmSRncEdn - newSEP.setRncEdn((short) operand); + newSEP.setRncEdn((short) sprm.getOperand()); break; case 0x3F: // [MS-DOC], v20140721, 2.6.4, sprmSNFtn - newSEP.setNFtn(operand); + newSEP.setNFtn((int) sprm.getOperand()); break; case 0x40: // [MS-DOC], v20140721, 2.6.4, sprmSNFtnRef - newSEP.setNfcFtnRef(operand); + newSEP.setNfcFtnRef((int) sprm.getOperand()); break; case 0x41: // [MS-DOC], v20140721, 2.6.4, sprmSNEdn - newSEP.setNEdn(operand); + newSEP.setNEdn((int) sprm.getOperand()); break; case 0x42: // [MS-DOC], v20140721, 2.6.4, sprmSNEdnRef - newSEP.setNfcEdnRef(operand); + newSEP.setNfcEdnRef((int) sprm.getOperand()); break; default: logger.log(POILogger.INFO, "Unsupported Sprm operation: " + operation + " (" + HexDump.byteToHex(operation) + ")"); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/extractor/TestExtractor.java b/src/scratchpad/testcases/org/apache/poi/hslf/extractor/TestExtractor.java index 5b78d1484c..5e6b71928d 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/extractor/TestExtractor.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/extractor/TestExtractor.java @@ -18,6 +18,7 @@ package org.apache.poi.hslf.extractor; import static org.apache.poi.POITestCase.assertContains; +import static org.apache.poi.POITestCase.assertContainsIgnoreCase; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -26,7 +27,6 @@ import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.util.List; -import java.util.Locale; import org.apache.poi.POIDataSamples; import org.apache.poi.hslf.model.OLEShape; @@ -355,7 +355,7 @@ public final class TestExtractor { ppe = new PowerPointExtractor(hslf); text = ppe.getText(); - assertContains(text.toLowerCase(Locale.ROOT), "master"); + assertContainsIgnoreCase(text, "master"); assertContains(text, masterText); } diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFWrite.java b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFWrite.java index 95d7919e33..a45b031da2 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFWrite.java +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHWPFWrite.java @@ -21,11 +21,16 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; +import org.apache.poi.POIDataSamples; import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.HWPFTestCase; import org.apache.poi.hwpf.HWPFTestDataSamples; +import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; +import org.apache.poi.poifs.filesystem.OPOIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.IOUtils; import org.apache.poi.util.TempFile; /** @@ -75,7 +80,65 @@ public final class TestHWPFWrite extends HWPFTestCase { r = doc.getRange(); assertEquals("I am a test document\r", r.getParagraph(0).text()); doc.close(); - } + } - // TODO In-place write positive and negative checks + /** + * Writing to the file we opened from - note, uses a temp file to + * avoid changing our test files! + */ + @SuppressWarnings("resource") + public void testInPlaceWrite() throws Exception { + // Setup as a copy of a known-good file + final File file = TempFile.createTempFile("TestDocument", ".doc"); + IOUtils.copy( + POIDataSamples.getDocumentInstance().openResourceAsStream("SampleDoc.doc"), + new FileOutputStream(file) + ); + + // Open from the temp file in read-write mode + HWPFDocument doc = new HWPFDocument(new NPOIFSFileSystem(file, false).getRoot()); + Range r = doc.getRange(); + assertEquals("I am a test document\r", r.getParagraph(0).text()); + + // Change + r.replaceText("X XX a test document\r", false); + + // Save in-place, close, re-open and check + doc.write(); + doc.close(); + + doc = new HWPFDocument(new NPOIFSFileSystem(file).getRoot()); + assertEquals("X XX a test document\r", r.getParagraph(0).text()); + } + + @SuppressWarnings("resource") + public void testInvalidInPlaceWrite() throws Exception { + HWPFDocument doc; + + // Can't work for InputStream opened files + doc = new HWPFDocument( + POIDataSamples.getDocumentInstance().openResourceAsStream("SampleDoc.doc")); + try { + doc.write(); + fail("Shouldn't work for InputStream"); + } catch (IllegalStateException e) {} + + // Can't work for OPOIFS + OPOIFSFileSystem ofs = new OPOIFSFileSystem( + POIDataSamples.getDocumentInstance().openResourceAsStream("SampleDoc.doc")); + doc = new HWPFDocument(ofs.getRoot()); + try { + doc.write(); + fail("Shouldn't work for OPOIFSFileSystem"); + } catch (IllegalStateException e) {} + + // Can't work for Read-Only files + NPOIFSFileSystem fs = new NPOIFSFileSystem( + POIDataSamples.getDocumentInstance().getFile("SampleDoc.doc"), true); + doc = new HWPFDocument(fs.getRoot()); + try { + doc.write(); + fail("Shouldn't work for Read Only"); + } catch (IllegalStateException e) {} + } } diff --git a/src/testcases/org/apache/poi/POITestCase.java b/src/testcases/org/apache/poi/POITestCase.java index 8fa16c88a8..786c6e1124 100644 --- a/src/testcases/org/apache/poi/POITestCase.java +++ b/src/testcases/org/apache/poi/POITestCase.java @@ -32,13 +32,16 @@ import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; import org.apache.poi.util.SuppressForbidden; +import org.apache.poi.util.Internal; /** * Util class for POI JUnit TestCases, which provide additional features */ +@Internal public final class POITestCase { public static void assertContains(String haystack, String needle) { assertNotNull(haystack); @@ -47,6 +50,19 @@ public final class POITestCase { haystack.contains(needle) ); } + + public static void assertContainsIgnoreCase(String haystack, String needle, Locale locale) { + assertNotNull(haystack); + assertNotNull(needle); + String hay = haystack.toLowerCase(locale); + String n = needle.toLowerCase(locale); + assertTrue("Unable to find expected text '" + needle + "' in text:\n" + haystack, + hay.contains(n) + ); + } + public static void assertContainsIgnoreCase(String haystack, String needle) { + assertContainsIgnoreCase(haystack, needle, Locale.ROOT); + } public static void assertNotContained(String haystack, String needle) { assertNotNull(haystack); diff --git a/src/testcases/org/apache/poi/TestPOITestCase.java b/src/testcases/org/apache/poi/TestPOITestCase.java new file mode 100644 index 0000000000..fd9f3d1ad9 --- /dev/null +++ b/src/testcases/org/apache/poi/TestPOITestCase.java @@ -0,0 +1,126 @@ +/* ==================================================================== + 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Collections; +import java.util.Locale; +import java.util.Map; + +import org.apache.poi.POITestCase; +import org.junit.Ignore; +import org.junit.Test; + +/** + * A class for testing the POI Junit TestCase utility class + */ +public final class TestPOITestCase { + @Test + public void assertContains() { + POITestCase.assertContains("There is a needle in this haystack", "needle"); + /*try { + POITestCase.assertContains("There is gold in this haystack", "needle"); + fail("found a needle"); + } catch (final junit.framework.AssertionFailedError e) { + // expected + }*/ + } + + @Test + public void assertContainsIgnoreCase_Locale() { + POITestCase.assertContainsIgnoreCase("There is a Needle in this haystack", "needlE", Locale.ROOT); + // FIXME: test failing case + } + + @Test + public void assertContainsIgnoreCase() { + POITestCase.assertContainsIgnoreCase("There is a Needle in this haystack", "needlE"); + // FIXME: test failing case + } + + @Test + public void assertNotContained() { + POITestCase.assertNotContained("There is a needle in this haystack", "gold"); + // FIXME: test failing case + } + + @Test + public void assertMapContains() { + Map haystack = Collections.singletonMap("needle", "value"); + POITestCase.assertContains(haystack, "needle"); + // FIXME: test failing case + } + + + /** + * Utility method to get the value of a private/protected field. + * Only use this method in test cases!!! + */ + @Ignore + @Test + public void getFieldValue() { + /* + final Class clazz; + final T instance; + final Class fieldType; + final String fieldName; + + final R expected; + final R actual = POITestCase.getFieldValue(clazz, instance, fieldType, fieldName); + assertEquals(expected, actual); + */ + } + + /** + * Utility method to call a private/protected method. + * Only use this method in test cases!!! + */ + @Ignore + @Test + public void callMethod() { + /* + final Class clazz; + final T instance; + final Class returnType; + final String methodName; + final Class[] parameterTypes; + final Object[] parameters; + + final R expected; + final R actual = POITestCase.callMethod(clazz, instance, returnType, methodName, parameterTypes, parameters); + assertEquals(expected, actual); + */ + } + + /** + * Utility method to shallow compare all fields of the objects + * Only use this method in test cases!!! + */ + @Ignore + @Test + public void assertReflectEquals() throws Exception { + /* + final Object expected; + final Object actual; + POITestCase.assertReflectEquals(expected, actual); + */ + } +}