Modified: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java?rev=1686117&r1=1686116&r2=1686117&view=diff ============================================================================== --- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java (original) +++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java Wed Jun 17 22:21:13 2015 @@ -34,7 +34,7 @@ import org.apache.poi.util.*; * This class represents a run of text in a powerpoint document. That * run could be text on a sheet, or text in a note. * It is only a very basic class for now - * + * * @author Nick Burch */ @@ -44,50 +44,49 @@ public final class HSLFTextParagraph imp /** * How to align the text */ - /* package */ static final int AlignLeft = 0; - /* package */ static final int AlignCenter = 1; - /* package */ static final int AlignRight = 2; - /* package */ static final int AlignJustify = 3; - + /* package */static final int AlignLeft = 0; + /* package */static final int AlignCenter = 1; + /* package */static final int AlignRight = 2; + /* package */static final int AlignJustify = 3; // Note: These fields are protected to help with unit testing - // Other classes shouldn't really go playing with them! - private final TextHeaderAtom _headerAtom; - private TextBytesAtom _byteAtom; - private TextCharsAtom _charAtom; - private final TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph); + // Other classes shouldn't really go playing with them! + private final TextHeaderAtom _headerAtom; + private TextBytesAtom _byteAtom; + private TextCharsAtom _charAtom; + private final TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph); protected TextRulerAtom _ruler; - protected List<HSLFTextRun> _runs = new ArrayList<HSLFTextRun>(); - protected HSLFTextShape _parentShape; + protected final List<HSLFTextRun> _runs = new ArrayList<HSLFTextRun>(); + protected HSLFTextShape _parentShape; private HSLFSheet _sheet; private int shapeId; - // private StyleTextPropAtom styleTextPropAtom; - private StyleTextProp9Atom styleTextProp9Atom; + // private StyleTextPropAtom styleTextPropAtom; + private StyleTextProp9Atom styleTextProp9Atom; - /** + /** * Constructs a Text Run from a Unicode text block. * Either a {@link TextCharsAtom} or a {@link TextBytesAtom} needs to be provided. - * + * * @param tha the TextHeaderAtom that defines what's what * @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided * @param tca the TextCharsAtom containing the text or null if {@link TextBytesAtom} is provided - */ + */ /* package */ HSLFTextParagraph( TextHeaderAtom tha, TextBytesAtom tba, TextCharsAtom tca ) { - if (tha == null) { - throw new IllegalArgumentException("TextHeaderAtom must be set."); - } - _headerAtom = tha; + if (tha == null) { + throw new IllegalArgumentException("TextHeaderAtom must be set."); + } + _headerAtom = tha; _byteAtom = tba; _charAtom = tca; - } + } - /* package */ HSLFTextParagraph(HSLFTextParagraph other) { + /* package */HSLFTextParagraph(HSLFTextParagraph other) { _headerAtom = other._headerAtom; _byteAtom = other._byteAtom; _charAtom = other._charAtom; @@ -98,67 +97,67 @@ public final class HSLFTextParagraph imp _paragraphStyle.copy(other._paragraphStyle); } - public void addTextRun(HSLFTextRun run) { - _runs.add(run); - } + public void addTextRun(HSLFTextRun run) { + _runs.add(run); + } - /** + /** * Fetch the rich text runs (runs of text with the same styling) that * are contained within this block of text - */ - public List<HSLFTextRun> getTextRuns() { - return _runs; - } - - public TextPropCollection getParagraphStyle() { - return _paragraphStyle; - } - - public void setParagraphStyle(TextPropCollection paragraphStyle) { - _paragraphStyle.copy(paragraphStyle); - } + */ + public List<HSLFTextRun> getTextRuns() { + return _runs; + } - /** + public TextPropCollection getParagraphStyle() { + return _paragraphStyle; + } + + public void setParagraphStyle(TextPropCollection paragraphStyle) { + _paragraphStyle.copy(paragraphStyle); + } + + /** * Supply the Sheet we belong to, which might have an assigned SlideShow * Also passes it on to our child RichTextRuns */ - public void supplySheet(HSLFSheet sheet){ + public void supplySheet(HSLFSheet sheet) { this._sheet = sheet; if (_runs == null) return; - for(HSLFTextRun rt : _runs) { + for (HSLFTextRun rt : _runs) { rt.updateSheet(); } - } + } - public HSLFSheet getSheet(){ + public HSLFSheet getSheet() { return this._sheet; } /** - * @return Shape ID + * @return Shape ID */ - protected int getShapeId(){ + protected int getShapeId() { return shapeId; } /** - * @param id Shape ID + * @param id Shape ID */ - protected void setShapeId(int id){ + protected void setShapeId(int id) { shapeId = id; } /** - * @return 0-based index of the text run in the SLWT container + * @return 0-based index of the text run in the SLWT container */ - protected int getIndex(){ + protected int getIndex() { return (_headerAtom != null) ? _headerAtom.getIndex() : -1; } /** * Sets the index of the paragraph in the SLWT container - * + * * @param index */ protected void setIndex(int index) { @@ -166,10 +165,10 @@ public final class HSLFTextParagraph imp } /** - * Returns the type of the text, from the TextHeaderAtom. - * Possible values can be seen from TextHeaderAtom - * @see org.apache.poi.hslf.record.TextHeaderAtom - */ + * Returns the type of the text, from the TextHeaderAtom. + * Possible values can be seen from TextHeaderAtom + * @see org.apache.poi.hslf.record.TextHeaderAtom + */ public int getRunType() { return (_headerAtom != null) ? _headerAtom.getTextType() : -1; } @@ -177,8 +176,7 @@ public final class HSLFTextParagraph imp public void setRunType(int runType) { if (_headerAtom != null) _headerAtom.setTextType(runType); } - - + /** * Is this Text Run one from a {@link PPDrawing}, or is it * one from the {@link SlideListWithText}? @@ -187,12 +185,12 @@ public final class HSLFTextParagraph imp return (getIndex() == -1); } - public TextRulerAtom getTextRuler(){ + public TextRulerAtom getTextRuler() { return _ruler; } - public TextRulerAtom createTextRuler(){ + public TextRulerAtom createTextRuler() { _ruler = getTextRuler(); if (_ruler == null) { _ruler = TextRulerAtom.getParagraphInstance(); @@ -207,19 +205,19 @@ public final class HSLFTextParagraph imp /** * Returns records that make up the list of text paragraphs * (there can be misc InteractiveInfo, TxInteractiveInfo and other records) - * + * * @return text run records */ - public Record[] getRecords(){ + public Record[] getRecords() { Record r[] = _headerAtom.getParentRecord().getChildRecords(); - return getRecords(r, new int[]{0}, _headerAtom); + return getRecords(r, new int[] { 0 }, _headerAtom); } private static Record[] getRecords(Record[] records, int[] startIdx, TextHeaderAtom headerAtom) { if (records == null) { throw new NullPointerException("records need to be set."); } - + for (; startIdx[0] < records.length; startIdx[0]++) { Record r = records[startIdx[0]]; if (r instanceof TextHeaderAtom && (headerAtom == null || r == headerAtom)) break; @@ -229,135 +227,113 @@ public final class HSLFTextParagraph imp logger.log(POILogger.INFO, "header atom wasn't found - container might contain only an OutlineTextRefAtom"); return new Record[0]; } - + int length; - for (length = 1; startIdx[0]+length < records.length; length++) { + for (length = 1; startIdx[0] + length < records.length; length++) { if (records[startIdx[0]+length] instanceof TextHeaderAtom) break; } - + Record result[] = new Record[length]; System.arraycopy(records, startIdx[0], result, 0, length); startIdx[0] += length; - + return result; } - - /** Numbered List info */ - public void setStyleTextProp9Atom(final StyleTextProp9Atom styleTextProp9Atom) { - this.styleTextProp9Atom = styleTextProp9Atom; - } /** Numbered List info */ - public StyleTextProp9Atom getStyleTextProp9Atom() { - return this.styleTextProp9Atom; - } - - /** - * Fetch the value of the given Paragraph related TextProp. - * Returns -1 if that TextProp isn't present. - * If the TextProp isn't present, the value from the appropriate - * Master Sheet will apply. - */ - private int getParaTextPropVal(String propName) { - TextProp prop = _paragraphStyle.findByName(propName); - BitMaskTextProp maskProp = (BitMaskTextProp)_paragraphStyle.findByName(ParagraphFlagsTextProp.NAME); - boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0); - if (prop == null && !hardAttribute){ - HSLFSheet sheet = getSheet(); - int txtype = getRunType(); - HSLFMasterSheet master = sheet.getMasterSheet(); - if (master != null) - prop = master.getStyleAttribute(txtype, getIndentLevel(), propName, false); - } - - return prop == null ? -1 : prop.getValue(); + public void setStyleTextProp9Atom(final StyleTextProp9Atom styleTextProp9Atom) { + this.styleTextProp9Atom = styleTextProp9Atom; } - /** - * Sets the value of the given Character TextProp, add if required - * @param propName The name of the Character TextProp - * @param val The value to set for the TextProp - */ - public void setParaTextPropVal(String propName, int val) { - // Ensure we have the StyleTextProp atom we're going to need - assert(_paragraphStyle!=null); - TextProp tp = fetchOrAddTextProp(_paragraphStyle, propName); - tp.setValue(val); + /** Numbered List info */ + public StyleTextProp9Atom getStyleTextProp9Atom() { + return this.styleTextProp9Atom; } - + @Override public Iterator<HSLFTextRun> iterator() { return _runs.iterator(); } @Override - public double getLeftMargin() { - int val = getParaTextPropVal("text.offset"); - return val*HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI); + public Double getLeftMargin() { + TextProp val = getPropVal(_paragraphStyle, "text.offset", this); + return (val == null) ? null : Units.masterToPoints(val.getValue()); } @Override - public void setLeftMargin(double leftMargin) { - int val = (int)(leftMargin*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI); - setParaTextPropVal("text.offset", val); + public void setLeftMargin(Double leftMargin) { + Integer val = (leftMargin == null) ? null : Units.pointsToMaster(leftMargin); + setPropVal(_paragraphStyle, "text.offset", val); } @Override - public double getRightMargin() { + public Double getRightMargin() { // TODO: find out, how to determine this value - return 0; + return null; } @Override - public void setRightMargin(double rightMargin) { + public void setRightMargin(Double rightMargin) { // TODO: find out, how to set this value } @Override - public double getIndent() { - int val = getParaTextPropVal("bullet.offset"); - return val*HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI); + public Double getIndent() { + TextProp val = getPropVal(_paragraphStyle, "bullet.offset", this); + return (val == null) ? null : Units.masterToPoints(val.getValue()); } @Override - public void setIndent(double intent) { - int val = (int)(intent*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI); - setParaTextPropVal("bullet.offset", val); + public void setIndent(Double indent) { + Integer val = (indent == null) ? null : Units.pointsToMaster(indent); + setPropVal(_paragraphStyle, "bullet.offset", val); } @Override public String getDefaultFontFamily() { - return (_runs.isEmpty() ? "Arial" : _runs.get(0).getFontFamily()); + String typeface = null; + if (!_runs.isEmpty()) { + typeface = _runs.get(0).getFontFamily(); + } + return (typeface != null) ? typeface : "Arial"; } @Override - public double getDefaultFontSize() { - return (_runs.isEmpty() ? 12 : _runs.get(0).getFontSize()); + public Double getDefaultFontSize() { + Double d = null; + if (!_runs.isEmpty()) { + d = _runs.get(0).getFontSize(); + } + + return (d != null) ? d : 12d; } /** * Sets the type of horizontal alignment for the paragraph. - * + * * @param align - the type of alignment */ public void setAlignment(org.apache.poi.sl.usermodel.TextParagraph.TextAlign align) { - int alignInt; - switch (align) { - default: - case LEFT: alignInt = TextAlignmentProp.LEFT; break; - case CENTER: alignInt = TextAlignmentProp.CENTER; break; - case RIGHT: alignInt = TextAlignmentProp.RIGHT; break; - case DIST: alignInt = TextAlignmentProp.DISTRIBUTED; break; - case JUSTIFY: alignInt = TextAlignmentProp.JUSTIFY; break; - case JUSTIFY_LOW: alignInt = TextAlignmentProp.JUSTIFYLOW; break; - case THAI_DIST: alignInt = TextAlignmentProp.THAIDISTRIBUTED; break; + Integer alignInt = null; + if (align != null) switch (align) { + default: + case LEFT: alignInt = TextAlignmentProp.LEFT;break; + case CENTER: alignInt = TextAlignmentProp.CENTER; break; + case RIGHT: alignInt = TextAlignmentProp.RIGHT; break; + case DIST: alignInt = TextAlignmentProp.DISTRIBUTED; break; + case JUSTIFY: alignInt = TextAlignmentProp.JUSTIFY; break; + case JUSTIFY_LOW: alignInt = TextAlignmentProp.JUSTIFYLOW; break; + case THAI_DIST: alignInt = TextAlignmentProp.THAIDISTRIBUTED; break; } - setParaTextPropVal("alignment", alignInt); + setPropVal(_paragraphStyle, "alignment", alignInt); } @Override public org.apache.poi.sl.usermodel.TextParagraph.TextAlign getTextAlign() { - switch (getParaTextPropVal("alignment")) { + TextProp tp = getPropVal(_paragraphStyle, "alignment", this); + if (tp == null) return null; + switch (tp.getValue()) { default: case TextAlignmentProp.LEFT: return TextAlign.LEFT; case TextAlignmentProp.CENTER: return TextAlign.CENTER; @@ -371,34 +347,33 @@ public final class HSLFTextParagraph imp @Override public FontAlign getFontAlign() { - switch(getParaTextPropVal("fontAlign")) { - default: - case -1: return FontAlign.AUTO; + TextProp tp = getPropVal(_paragraphStyle, FontAlignmentProp.NAME, this); + if (tp == null) return null; + + switch (tp.getValue()) { case FontAlignmentProp.BASELINE: return FontAlign.BASELINE; case FontAlignmentProp.TOP: return FontAlign.TOP; case FontAlignmentProp.CENTER: return FontAlign.CENTER; case FontAlignmentProp.BOTTOM: return FontAlign.BOTTOM; + default: return FontAlign.AUTO; } } @Override public BulletStyle getBulletStyle() { - if (getBulletChar() == 0) return null; + if (!isBullet()) return null; return new BulletStyle() { public String getBulletCharacter() { - char chr = HSLFTextParagraph.this.getBulletChar(); - return (chr == 0 ? "" : ""+chr); + Character chr = HSLFTextParagraph.this.getBulletChar(); + return (chr == null || chr == 0) ? "" : "" + chr; } public String getBulletFont() { - int fontIdx = HSLFTextParagraph.this.getBulletFont(); - if (fontIdx == -1) return getDefaultFontFamily(); - PPFont ppFont = getSheet().getSlideShow().getFont(fontIdx); - return ppFont.getFontName(); + return HSLFTextParagraph.this.getBulletFont(); } - public double getBulletFontSize() { + public Double getBulletFontSize() { return HSLFTextParagraph.this.getBulletSize(); } @@ -417,666 +392,669 @@ public final class HSLFTextParagraph imp _parentShape = parentShape; } + /** + * + * @return indentation level + */ + public int getIndentLevel() { + return _paragraphStyle == null ? 0 : _paragraphStyle.getIndentLevel(); + } /** - * - * @return indentation level - */ - public int getIndentLevel() { - return _paragraphStyle == null ? 0 : _paragraphStyle.getIndentLevel(); - } - - /** - * Sets indentation level - * + * Sets indentation level + * * @param level indentation level. Must be in the range [0, 4] - */ - public void setIndentLevel(int level) { + */ + public void setIndentLevel(int level) { if( _paragraphStyle != null ) _paragraphStyle.setIndentLevel((short)level); - } + } + + /** + * Sets whether this rich text run has bullets + */ + public void setBullet(boolean flag) { + setFlag(ParagraphFlagsTextProp.BULLET_IDX, flag); + } + + /** + * Returns whether this rich text run has bullets + */ + public boolean isBullet() { + return getFlag(ParagraphFlagsTextProp.BULLET_IDX); + } + + /** + * Sets the bullet character + */ + public void setBulletChar(Character c) { + Integer val = (c == null) ? null : (int)c.charValue(); + setPropVal(_paragraphStyle, "bullet.char", val); + } + + /** + * Returns the bullet character + */ + public Character getBulletChar() { + TextProp tp = getPropVal(_paragraphStyle, "bullet.char", this); + return (tp == null) ? null : (char)tp.getValue(); + } + + /** + * Sets the bullet size + */ + public void setBulletSize(Double size) { + setPctOrPoints("bullet.size", size); + } + + /** + * Returns the bullet size, null if unset + */ + public Double getBulletSize() { + return getPctOrPoints("bullet.size"); + } + + /** + * Sets the bullet color + */ + public void setBulletColor(Color color) { + Integer val = (color == null) ? null : new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB(); + setPropVal(_paragraphStyle, "bullet.color", val); + } + + /** + * Returns the bullet color + */ + public Color getBulletColor() { + TextProp tp = getPropVal(_paragraphStyle, "bullet.color", this); + if (tp == null) { + // if bullet color is undefined, return color of first run + return (_runs.isEmpty()) ? null : _runs.get(0).getFontColor(); + } + + int rgb = tp.getValue(); + int cidx = rgb >> 24; + if (rgb % 0x1000000 == 0) { + if (_sheet == null) + return null; + ColorSchemeAtom ca = _sheet.getColorScheme(); + if (cidx >= 0 && cidx <= 7) + rgb = ca.getColor(cidx); + } + Color tmp = new Color(rgb, true); + return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed()); + } + + /** + * Sets the bullet font + */ + public void setBulletFont(String typeface) { + if (typeface == null) { + setPropVal(_paragraphStyle, "bullet.font", null); + setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, false); + } + + FontCollection fc = getSheet().getSlideShow().getFontCollection(); + int idx = fc.addFont(typeface); + + setPropVal(_paragraphStyle, "bullet.font", idx); + setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true); + } + + /** + * Returns the bullet font + */ + public String getBulletFont() { + TextProp tp = getPropVal(_paragraphStyle, "bullet.font", this); + if (tp == null) return getDefaultFontFamily(); + PPFont ppFont = getSheet().getSlideShow().getFont(tp.getValue()); + assert(ppFont != null); + return ppFont.getFontName(); + } + + @Override + public void setLineSpacing(Double lineSpacing) { + setPctOrPoints("linespacing", lineSpacing); + } + + @Override + public Double getLineSpacing() { + return getPctOrPoints("linespacing"); + } + + @Override + public void setSpaceBefore(Double spaceBefore) { + setPctOrPoints("spacebefore", spaceBefore); + } + + @Override + public Double getSpaceBefore() { + return getPctOrPoints("spacebefore"); + } + + @Override + public void setSpaceAfter(Double spaceAfter) { + setPctOrPoints("spaceafter", spaceAfter); + } + + @Override + public Double getSpaceAfter() { + return getPctOrPoints("spaceafter"); + } + + @Override + public Double getDefaultTabSize() { + // TODO: implement + return null; + } + + private Double getPctOrPoints(String propName) { + TextProp tp = getPropVal(_paragraphStyle, propName, this); + if (tp == null) return null; + int val = tp.getValue(); + return (val < 0) ? Units.masterToPoints(val) : val; + } + + private void setPctOrPoints(String propName, Double dval) { + Integer ival = null; + if (dval != null) { + ival = (dval < 0) ? Units.pointsToMaster(dval) : dval.intValue(); + } + setPropVal(_paragraphStyle, propName, ival); + } + + private boolean getFlag(int index) { + BitMaskTextProp tp = (BitMaskTextProp)getPropVal(_paragraphStyle, ParagraphFlagsTextProp.NAME, this); + return (tp == null) ? false : tp.getSubValue(index); + } + + private void setFlag(int index, boolean value) { + BitMaskTextProp tp = (BitMaskTextProp)_paragraphStyle.addWithName(ParagraphFlagsTextProp.NAME); + tp.setSubValue(value, index); + } + + /** + * Fetch the value of the given Paragraph related TextProp. Returns null if + * that TextProp isn't present. If the TextProp isn't present, the value + * from the appropriate Master Sheet will apply. + */ + protected static TextProp getPropVal(TextPropCollection props, String propName, HSLFTextParagraph paragraph) { + TextProp prop = props.findByName(propName); + if (prop != null) return prop; - /** - * Sets whether this rich text run has bullets - */ - public void setBullet(boolean flag) { - setFlag(ParagraphFlagsTextProp.BULLET_IDX, flag); - } - - /** - * Returns whether this rich text run has bullets - */ - public boolean isBullet() { - return getFlag(ParagraphFlagsTextProp.BULLET_IDX); - } - - /** - * Returns whether this rich text run has bullets - */ - public boolean isBulletHard() { - return getFlag(ParagraphFlagsTextProp.BULLET_IDX); - } - - /** - * Sets the bullet character - */ - public void setBulletChar(char c) { - setParaTextPropVal("bullet.char", c); - } - - /** - * Returns the bullet character - */ - public char getBulletChar() { - int val = getParaTextPropVal("bullet.char"); - return (char)(val == -1 ? 0 : val); - } - - /** - * Sets the bullet size - */ - public void setBulletSize(int size) { - setParaTextPropVal("bullet.size", size); - } - - /** - * Returns the bullet size - */ - public int getBulletSize() { - return getParaTextPropVal("bullet.size"); - } - - /** - * Sets the bullet color - */ - public void setBulletColor(Color color) { - int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB(); - setParaTextPropVal("bullet.color", rgb); - } - - /** - * Returns the bullet color - */ - public Color getBulletColor() { - int rgb = getParaTextPropVal("bullet.color"); - if (rgb == -1) { - // if bullet color is undefined, return color of first run - if (_runs.isEmpty()) return null; - return _runs.get(0).getFontColor(); - } - - int cidx = rgb >> 24; - if (rgb % 0x1000000 == 0){ - if (_sheet == null) return null; - ColorSchemeAtom ca = _sheet.getColorScheme(); - if(cidx >= 0 && cidx <= 7) rgb = ca.getColor(cidx); - } - Color tmp = new Color(rgb, true); - return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed()); - } - - /** - * Sets the bullet font - */ - public void setBulletFont(int idx) { - setParaTextPropVal("bullet.font", idx); - setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true); - } - - /** - * Returns the bullet font - */ - public int getBulletFont() { - return getParaTextPropVal("bullet.font"); - } - - @Override - public void setLineSpacing(double lineSpacing) { - // if lineSpacing < 0, we need to convert points (common interface) to master units (hslf) - if (lineSpacing < 0) { - lineSpacing = (lineSpacing*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI); - } - setParaTextPropVal("linespacing", (int)lineSpacing); - } - - @Override - public double getLineSpacing() { - double val = getParaTextPropVal("linespacing"); - // if lineSpacing < 0, we need to convert master units (hslf) to points (common interface) - if (val == -1) return 0; - if (val < -1) val *= HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI); - return val; - } - - /** - * Sets spacing before a paragraph. - * <p> - * If spacebefore >= 0, then spacebefore is a percentage of normal line height. - * If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates. - * </p> - */ - public void setSpaceBefore(int val) { - setParaTextPropVal("spacebefore", val); - } - - /** - * Returns spacing before a paragraph - * <p> - * If spacebefore >= 0, then spacebefore is a percentage of normal line height. - * If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates. - * </p> - * - * @return the spacing before a paragraph - */ - @Override - public double getSpaceBefore() { - int val = getParaTextPropVal("spacebefore"); - return val == -1 ? 0 : val; - } - - /** - * Sets spacing after a paragraph. - * <p> - * If spaceafter >= 0, then spaceafter is a percentage of normal line height. - * If spaceafter < 0, the absolute value of spaceafter is the spacing in master coordinates. - * </p> - */ - public void setSpaceAfter(int val) { - setParaTextPropVal("spaceafter", val); - } - - /** - * Returns spacing after a paragraph - * <p> - * If spaceafter >= 0, then spaceafter is a percentage of normal line height. - * If spaceafter < 0, the absolute value of spaceafter is the spacing in master coordinates. - * </p> - * - * @return the spacing before a paragraph - */ - @Override - public double getSpaceAfter() { - int val = getParaTextPropVal("spaceafter"); - return val == -1 ? 0 : val; - } - - /** - * Returns the named TextProp, either by fetching it (if it exists) or adding it - * (if it didn't) - * @param textPropCol The TextPropCollection to fetch from / add into - * @param textPropName The name of the TextProp to fetch/add - */ - protected static TextProp fetchOrAddTextProp(TextPropCollection textPropCol, String textPropName) { + BitMaskTextProp maskProp = (BitMaskTextProp) props.findByName(ParagraphFlagsTextProp.NAME); + boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0); + if (hardAttribute) return null; + + HSLFSheet sheet = paragraph.getSheet(); + int txtype = paragraph.getRunType(); + HSLFMasterSheet master = sheet.getMasterSheet(); + if (master == null) { + logger.log(POILogger.WARN, "MasterSheet is not available"); + return null; + } + + boolean isChar = props.getTextPropType() == TextPropType.character; + return master.getStyleAttribute(txtype, paragraph.getIndentLevel(), propName, isChar); + } + + /** + * Returns the named TextProp, either by fetching it (if it exists) or + * adding it (if it didn't) + * + * @param props the TextPropCollection to fetch from / add into + * @param name the name of the TextProp to fetch/add + * @param val the value, null if unset + */ + protected static void setPropVal(TextPropCollection props, String name, Integer val) { + if (val == null) { + props.removeByName(name); + return; + } + // Fetch / Add the TextProp - return textPropCol.addWithName(textPropName); + TextProp tp = props.addWithName(name); + tp.setValue(val); } + + /** + * Check and add linebreaks to text runs leading other paragraphs + * + * @param paragraphs + */ + protected static void fixLineEndings(List<HSLFTextParagraph> paragraphs) { + HSLFTextRun lastRun = null; + for (HSLFTextParagraph p : paragraphs) { + if (lastRun != null && !lastRun.getRawText().endsWith("\r")) { + lastRun.setText(lastRun.getRawText() + "\r"); + } + List<HSLFTextRun> ltr = p.getTextRuns(); + if (ltr.isEmpty()) { + throw new RuntimeException("paragraph without textruns found"); + } + lastRun = ltr.get(ltr.size() - 1); + assert (lastRun.getRawText() != null); + } + } + + /** + * Search for a StyleTextPropAtom is for this text header (list of paragraphs) + * + * @param header the header + * @param textLen the length of the rawtext, or -1 if the length is not known + */ + private static StyleTextPropAtom findStyleAtomPresent(TextHeaderAtom header, int textLen) { + boolean afterHeader = false; + StyleTextPropAtom style = null; + for (Record record : header.getParentRecord().getChildRecords()) { + long rt = record.getRecordType(); + if (afterHeader && rt == RecordTypes.TextHeaderAtom.typeID) { + // already on the next header, quit searching + break; + } + afterHeader |= (header == record); + if (afterHeader && rt == RecordTypes.StyleTextPropAtom.typeID) { + // found it + style = (StyleTextPropAtom) record; + } + } + + if (style == null) { + logger.log(POILogger.INFO, "styles atom doesn't exist. Creating dummy record for later saving."); + style = new StyleTextPropAtom((textLen < 0) ? 1 : textLen); + } else { + if (textLen >= 0) { + style.setParentTextSize(textLen); + } + } - protected boolean getFlag(int index) { - if (_paragraphStyle == null) return false; + return style; + } - BitMaskTextProp prop = (BitMaskTextProp) _paragraphStyle.findByName(ParagraphFlagsTextProp.NAME); + /** + * Saves the modified paragraphs/textrun to the records. + * Also updates the styles to the correct text length. + */ + protected static void storeText(List<HSLFTextParagraph> paragraphs) { + fixLineEndings(paragraphs); - if (prop == null) { - if (_sheet != null) { - int txtype = getRunType(); - HSLFMasterSheet master = _sheet.getMasterSheet(); - if (master != null) { - prop = (BitMaskTextProp) master.getStyleAttribute(txtype, getIndentLevel(), ParagraphFlagsTextProp.NAME, false); - } + String rawText = toInternalString(getRawText(paragraphs)); + + // Will it fit in a 8 bit atom? + boolean isUnicode = StringUtil.hasMultibyte(rawText); + // isUnicode = true; + + TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom; + TextBytesAtom byteAtom = paragraphs.get(0)._byteAtom; + TextCharsAtom charAtom = paragraphs.get(0)._charAtom; + StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length()); + + // Store in the appropriate record + Record oldRecord = null, newRecord = null; + if (isUnicode) { + if (byteAtom != null || charAtom == null) { + oldRecord = byteAtom; + charAtom = new TextCharsAtom(); + } + newRecord = charAtom; + charAtom.setText(rawText); + } else { + if (charAtom != null || byteAtom == null) { + oldRecord = charAtom; + byteAtom = new TextBytesAtom(); + } + newRecord = byteAtom; + byte[] byteText = new byte[rawText.length()]; + StringUtil.putCompressedUnicode(rawText, byteText, 0); + byteAtom.setText(byteText); + } + assert (newRecord != null); + + RecordContainer _txtbox = headerAtom.getParentRecord(); + Record[] cr = _txtbox.getChildRecords(); + int headerIdx = -1, textIdx = -1, styleIdx = -1; + for (int i = 0; i < cr.length; i++) { + Record r = cr[i]; + if (r == headerAtom) headerIdx = i; + else if (r == oldRecord || r == newRecord) textIdx = i; + else if (r == styleAtom) styleIdx = i; + } + + if (textIdx == -1) { + // the old record was never registered, ignore it + _txtbox.addChildAfter(newRecord, headerAtom); + textIdx = headerIdx + 1; + } else { + // swap not appropriated records - noop if unchanged + cr[textIdx] = newRecord; + } + + if (styleIdx == -1) { + // Add the new StyleTextPropAtom after the TextCharsAtom / TextBytesAtom + _txtbox.addChildAfter(styleAtom, newRecord); + } + + for (HSLFTextParagraph p : paragraphs) { + if (newRecord == byteAtom) { + p._byteAtom = byteAtom; + p._charAtom = null; } else { - logger.log(POILogger.WARN, "MasterSheet is not available"); + p._byteAtom = null; + p._charAtom = charAtom; + } + } + + // Update the text length for its Paragraph and Character stylings + // * reset the length, to the new string's length + // * add on +1 if the last block + + styleAtom.clearStyles(); + + TextPropCollection lastPTPC = null, lastRTPC = null, ptpc = null, rtpc = null; + for (HSLFTextParagraph para : paragraphs) { + ptpc = para.getParagraphStyle(); + ptpc.updateTextSize(0); + if (!ptpc.equals(lastPTPC)) { + lastPTPC = styleAtom.addParagraphTextPropCollection(0); + lastPTPC.copy(ptpc); + } + for (HSLFTextRun tr : para.getTextRuns()) { + rtpc = tr.getCharacterStyle(); + rtpc.updateTextSize(0); + if (!rtpc.equals(lastRTPC)) { + lastRTPC = styleAtom.addCharacterTextPropCollection(0); + lastRTPC.copy(rtpc); + } + int len = tr.getLength(); + ptpc.updateTextSize(ptpc.getCharactersCovered() + len); + rtpc.updateTextSize(len); + lastPTPC.updateTextSize(lastPTPC.getCharactersCovered() + len); + lastRTPC.updateTextSize(lastRTPC.getCharactersCovered() + len); } } - return prop == null ? false : prop.getSubValue(index); + assert (lastPTPC != null && lastRTPC != null && ptpc != null && rtpc != null); + ptpc.updateTextSize(ptpc.getCharactersCovered() + 1); + rtpc.updateTextSize(rtpc.getCharactersCovered() + 1); + lastPTPC.updateTextSize(lastPTPC.getCharactersCovered() + 1); + lastRTPC.updateTextSize(lastRTPC.getCharactersCovered() + 1); + + /** + * If TextSpecInfoAtom is present, we must update the text size in it, + * otherwise the ppt will be corrupted + */ + for (Record r : paragraphs.get(0).getRecords()) { + if (r instanceof TextSpecInfoAtom) { + ((TextSpecInfoAtom) r).setParentSize(rawText.length() + 1); + break; + } + } + + if (_txtbox instanceof EscherTextboxWrapper) { + try { + ((EscherTextboxWrapper) _txtbox).writeOut(null); + } catch (IOException e) { + throw new RuntimeException("failed dummy write", e); + } + } } - protected void setFlag(int index, boolean value) { - // Ensure we have the StyleTextProp atom we're going to need - assert(_paragraphStyle!=null); - BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(_paragraphStyle, ParagraphFlagsTextProp.NAME); - prop.setSubValue(value,index); - } - - /** - * Check and add linebreaks to text runs leading other paragraphs - * - * @param paragraphs - */ - protected static void fixLineEndings(List<HSLFTextParagraph> paragraphs) { - HSLFTextRun lastRun = null; - for (HSLFTextParagraph p : paragraphs) { - if (lastRun != null && !lastRun.getRawText().endsWith("\r")) { - lastRun.setText(lastRun.getRawText()+"\r"); - } - List<HSLFTextRun> ltr = p.getTextRuns(); - if (ltr.isEmpty()) { - throw new RuntimeException("paragraph without textruns found"); - } - lastRun = ltr.get(ltr.size()-1); - assert(lastRun.getRawText() != null); - } - } - - /** - * Search for a StyleTextPropAtom is for this text header (list of paragraphs) - * - * @param header the header - * @param textLen the length of the rawtext, or -1 if the length is not known - */ - private static StyleTextPropAtom findStyleAtomPresent(TextHeaderAtom header, int textLen) { - boolean afterHeader = false; - StyleTextPropAtom style = null; - for (Record record : header.getParentRecord().getChildRecords()) { - long rt = record.getRecordType(); - if (afterHeader && rt == RecordTypes.TextHeaderAtom.typeID) { - // already on the next header, quit searching - break; - } - afterHeader |= (header == record); - if (afterHeader && rt == RecordTypes.StyleTextPropAtom.typeID) { - // found it - style = (StyleTextPropAtom)record; - } - } - - if (style == null) { - logger.log(POILogger.INFO, "styles atom doesn't exist. Creating dummy record for later saving."); - style = new StyleTextPropAtom((textLen < 0) ? 1 : textLen); - } else { - if (textLen >= 0) { - style.setParentTextSize(textLen); - } - } - - return style; - } - - - /** - * Saves the modified paragraphs/textrun to the records. - * Also updates the styles to the correct text length. - */ - protected static void storeText(List<HSLFTextParagraph> paragraphs) { - fixLineEndings(paragraphs); - - String rawText = toInternalString(getRawText(paragraphs)); - - // Will it fit in a 8 bit atom? - boolean isUnicode = StringUtil.hasMultibyte(rawText); - // isUnicode = true; - - TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom; - TextBytesAtom byteAtom = paragraphs.get(0)._byteAtom; - TextCharsAtom charAtom = paragraphs.get(0)._charAtom; - StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length()); - - // Store in the appropriate record - Record oldRecord = null, newRecord = null; - if (isUnicode) { - if (byteAtom != null || charAtom == null) { - oldRecord = byteAtom; - charAtom = new TextCharsAtom(); - } - newRecord = charAtom; - charAtom.setText(rawText); - } else { - if (charAtom != null || byteAtom == null) { - oldRecord = charAtom; - byteAtom = new TextBytesAtom(); - } - newRecord = byteAtom; - byte[] byteText = new byte[rawText.length()]; - StringUtil.putCompressedUnicode(rawText,byteText,0); - byteAtom.setText(byteText); - } - assert(newRecord != null); - - RecordContainer _txtbox = headerAtom.getParentRecord(); - Record[] cr = _txtbox.getChildRecords(); - int headerIdx = -1, textIdx = -1, styleIdx = -1; - for (int i=0; i<cr.length; i++) { - Record r = cr[i]; - if (r == headerAtom) headerIdx = i; - else if (r == oldRecord || r == newRecord) textIdx = i; - else if (r == styleAtom) styleIdx = i; - } - - if (textIdx == -1) { - // the old record was never registered, ignore it - _txtbox.addChildAfter(newRecord, headerAtom); - textIdx = headerIdx+1; - } else { - // swap not appropriated records - noop if unchanged - cr[textIdx] = newRecord; - } - - if (styleIdx == -1) { - // Add the new StyleTextPropAtom after the TextCharsAtom / TextBytesAtom - _txtbox.addChildAfter(styleAtom, newRecord); - } - - for (HSLFTextParagraph p : paragraphs) { - if (newRecord == byteAtom) { - p._byteAtom = byteAtom; - p._charAtom = null; - } else { - p._byteAtom = null; - p._charAtom = charAtom; - } - } - - // Update the text length for its Paragraph and Character stylings - // * reset the length, to the new string's length - // * add on +1 if the last block - - styleAtom.clearStyles(); - - TextPropCollection lastPTPC = null, lastRTPC = null, ptpc = null, rtpc = null; - for (HSLFTextParagraph para : paragraphs) { - ptpc = para.getParagraphStyle(); - ptpc.updateTextSize(0); - if (!ptpc.equals(lastPTPC)) { - lastPTPC = styleAtom.addParagraphTextPropCollection(0); - lastPTPC.copy(ptpc); - } - for (HSLFTextRun tr : para.getTextRuns()) { - rtpc = tr.getCharacterStyle(); - rtpc.updateTextSize(0); - if (!rtpc.equals(lastRTPC)) { - lastRTPC = styleAtom.addCharacterTextPropCollection(0); - lastRTPC.copy(rtpc); - } - int len = tr.getLength(); - ptpc.updateTextSize(ptpc.getCharactersCovered()+len); - rtpc.updateTextSize(len); - lastPTPC.updateTextSize(lastPTPC.getCharactersCovered()+len); - lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+len); - } - } - - assert(lastPTPC != null && lastRTPC != null && ptpc != null && rtpc != null); - ptpc.updateTextSize(ptpc.getCharactersCovered()+1); - rtpc.updateTextSize(rtpc.getCharactersCovered()+1); - lastPTPC.updateTextSize(lastPTPC.getCharactersCovered()+1); - lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+1); - - /** - * If TextSpecInfoAtom is present, we must update the text size in it, - * otherwise the ppt will be corrupted - */ - for (Record r : paragraphs.get(0).getRecords()) { - if (r instanceof TextSpecInfoAtom) { - ((TextSpecInfoAtom)r).setParentSize(rawText.length()+1); - break; - } - } - - if (_txtbox instanceof EscherTextboxWrapper) { - try { - ((EscherTextboxWrapper)_txtbox).writeOut(null); - } catch (IOException e) { - throw new RuntimeException("failed dummy write", e); - } - } - } - - /** - * Adds the supplied text onto the end of the TextParagraphs, - * creating a new RichTextRun for it to sit in. - * - * @param text the text string used by this object. - */ - protected static HSLFTextRun appendText(List<HSLFTextParagraph> paragraphs, String text, boolean newParagraph) { - text = toInternalString(text); - - // check paragraphs - assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty()); - - HSLFTextParagraph htp = paragraphs.get(paragraphs.size()-1); - HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size()-1); - - boolean isFirst = !newParagraph; - for (String rawText : text.split("(?<=\r)")) { - if (!isFirst) { - TextPropCollection tpc = htp.getParagraphStyle(); - HSLFTextParagraph prevHtp = htp; - htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom); - htp.getParagraphStyle().copy(tpc); - htp.setParentShape(prevHtp.getParentShape()); - htp.setShapeId(prevHtp.getShapeId()); - htp.supplySheet(prevHtp.getSheet()); - paragraphs.add(htp); - } - isFirst = false; - - TextPropCollection tpc = htr.getCharacterStyle(); - // special case, last text run is empty, we will reuse it - if (htr.getLength() > 0) { - htr = new HSLFTextRun(htp); - htr.getCharacterStyle().copy(tpc); - htp.addTextRun(htr); - } - htr.setText(rawText); - } - - storeText(paragraphs); - - return htr; - } - - /** - * Sets (overwrites) the current text. - * Uses the properties of the first paragraph / textrun - * - * @param text the text string used by this object. - */ - public static HSLFTextRun setText(List<HSLFTextParagraph> paragraphs, String text) { - // check paragraphs - assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty()); - - Iterator<HSLFTextParagraph> paraIter = paragraphs.iterator(); - HSLFTextParagraph htp = paraIter.next(); // keep first - assert(htp != null); - while (paraIter.hasNext()) { - paraIter.next(); - paraIter.remove(); - } - - Iterator<HSLFTextRun> runIter = htp.getTextRuns().iterator(); - HSLFTextRun htr = runIter.next(); - htr.setText(""); - assert(htr != null); - while (runIter.hasNext()) { - runIter.next(); - runIter.remove(); - } - - return appendText(paragraphs, text, false); - } - - public static String getText(List<HSLFTextParagraph> paragraphs) { - assert(!paragraphs.isEmpty()); - String rawText = getRawText(paragraphs); - return toExternalString(rawText, paragraphs.get(0).getRunType()); - } - - public static String getRawText(List<HSLFTextParagraph> paragraphs) { - StringBuilder sb = new StringBuilder(); - for (HSLFTextParagraph p : paragraphs) { - for (HSLFTextRun r : p.getTextRuns()) { - sb.append(r.getRawText()); - } - } - return sb.toString(); - } - - /** - * Returns a new string with line breaks converted into internal ppt - * representation - */ - protected static String toInternalString(String s) { - String ns = s.replaceAll("\\r?\\n", "\r"); - return ns; - } - - /** - * Converts raw text from the text paragraphs to a formatted string, - * i.e. it converts certain control characters used in the raw txt - * - * @param rawText the raw text - * @param runType the run type of the shape, paragraph or headerAtom. - * use -1 if unknown - * @return the formatted string - */ - public static String toExternalString(String rawText, int runType) { - // PowerPoint seems to store files with \r as the line break - // The messes things up on everything but a Mac, so translate - // them to \n - String text = rawText.replace('\r', '\n'); - - switch (runType) { - // 0xB acts like cariage return in page titles and like blank in the - // others - case -1: - case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE: - case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE: - text = text.replace((char) 0x0B, '\n'); - break; - default: - text = text.replace((char) 0x0B, ' '); - break; - } - - return text; - } - - /** - * For a given PPDrawing, grab all the TextRuns - */ + /** + * Adds the supplied text onto the end of the TextParagraphs, + * creating a new RichTextRun for it to sit in. + * + * @param text the text string used by this object. + */ + protected static HSLFTextRun appendText(List<HSLFTextParagraph> paragraphs, String text, boolean newParagraph) { + text = toInternalString(text); + + // check paragraphs + assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty()); + + HSLFTextParagraph htp = paragraphs.get(paragraphs.size() - 1); + HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size() - 1); + + boolean isFirst = !newParagraph; + for (String rawText : text.split("(?<=\r)")) { + if (!isFirst) { + TextPropCollection tpc = htp.getParagraphStyle(); + HSLFTextParagraph prevHtp = htp; + htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom); + htp.getParagraphStyle().copy(tpc); + htp.setParentShape(prevHtp.getParentShape()); + htp.setShapeId(prevHtp.getShapeId()); + htp.supplySheet(prevHtp.getSheet()); + paragraphs.add(htp); + } + isFirst = false; + + TextPropCollection tpc = htr.getCharacterStyle(); + // special case, last text run is empty, we will reuse it + if (htr.getLength() > 0) { + htr = new HSLFTextRun(htp); + htr.getCharacterStyle().copy(tpc); + htp.addTextRun(htr); + } + htr.setText(rawText); + } + + storeText(paragraphs); + + return htr; + } + + /** + * Sets (overwrites) the current text. + * Uses the properties of the first paragraph / textrun + * + * @param text the text string used by this object. + */ + public static HSLFTextRun setText(List<HSLFTextParagraph> paragraphs, String text) { + // check paragraphs + assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty()); + + Iterator<HSLFTextParagraph> paraIter = paragraphs.iterator(); + HSLFTextParagraph htp = paraIter.next(); // keep first + assert (htp != null); + while (paraIter.hasNext()) { + paraIter.next(); + paraIter.remove(); + } + + Iterator<HSLFTextRun> runIter = htp.getTextRuns().iterator(); + HSLFTextRun htr = runIter.next(); + htr.setText(""); + assert (htr != null); + while (runIter.hasNext()) { + runIter.next(); + runIter.remove(); + } + + return appendText(paragraphs, text, false); + } + + public static String getText(List<HSLFTextParagraph> paragraphs) { + assert (!paragraphs.isEmpty()); + String rawText = getRawText(paragraphs); + return toExternalString(rawText, paragraphs.get(0).getRunType()); + } + + public static String getRawText(List<HSLFTextParagraph> paragraphs) { + StringBuilder sb = new StringBuilder(); + for (HSLFTextParagraph p : paragraphs) { + for (HSLFTextRun r : p.getTextRuns()) { + sb.append(r.getRawText()); + } + } + return sb.toString(); + } + + /** + * Returns a new string with line breaks converted into internal ppt + * representation + */ + protected static String toInternalString(String s) { + String ns = s.replaceAll("\\r?\\n", "\r"); + return ns; + } + + /** + * Converts raw text from the text paragraphs to a formatted string, + * i.e. it converts certain control characters used in the raw txt + * + * @param rawText the raw text + * @param runType the run type of the shape, paragraph or headerAtom. + * use -1 if unknown + * @return the formatted string + */ + public static String toExternalString(String rawText, int runType) { + // PowerPoint seems to store files with \r as the line break + // The messes things up on everything but a Mac, so translate + // them to \n + String text = rawText.replace('\r', '\n'); + + switch (runType) { + // 0xB acts like cariage return in page titles and like blank in the + // others + case -1: + case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE: + case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE: + text = text.replace((char) 0x0B, '\n'); + break; + default: + text = text.replace((char) 0x0B, ' '); + break; + } + + return text; + } + + /** + * For a given PPDrawing, grab all the TextRuns + */ public static List<List<HSLFTextParagraph>> findTextParagraphs(PPDrawing ppdrawing, HSLFSheet sheet) { - List<List<HSLFTextParagraph>> runsV = new ArrayList<List<HSLFTextParagraph>>(); - for (EscherTextboxWrapper wrapper : ppdrawing.getTextboxWrappers()) { - runsV.add(findTextParagraphs(wrapper, sheet)); - } - return runsV; - } - - /** - * Scans through the supplied record array, looking for - * a TextHeaderAtom followed by one of a TextBytesAtom or - * a TextCharsAtom. Builds up TextRuns from these - * - * @param wrapper an EscherTextboxWrapper - */ - protected static List<HSLFTextParagraph> findTextParagraphs(EscherTextboxWrapper wrapper, HSLFSheet sheet) { - // propagate parents to parent-aware records - RecordContainer.handleParentAwareRecords(wrapper); - int shapeId = wrapper.getShapeId(); - List<HSLFTextParagraph> rv = null; - - OutlineTextRefAtom ota = (OutlineTextRefAtom)wrapper.findFirstOfType(OutlineTextRefAtom.typeID); - if (ota != null) { - // if we are based on an outline, there are no further records to be parsed from the wrapper - if (sheet == null) { - throw new RuntimeException("Outline atom reference can't be solved without a sheet record"); - } - - List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs(); - assert(sheetRuns != null); - - int idx = ota.getTextIndex(); - for (List<HSLFTextParagraph> r : sheetRuns) { - if (r.isEmpty()) continue; - int ridx = r.get(0).getIndex(); - if (ridx > idx) break; - if (ridx == idx) { - if (rv == null) { - rv = r; - } else { - // create a new container - // TODO: ... is this case really happening? - rv = new ArrayList<HSLFTextParagraph>(rv); - rv.addAll(r); - } - } - } - if(rv == null || rv.isEmpty()) { - logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx); - } - } else { - if (sheet != null) { - // check sheet runs first, so we get exactly the same paragraph list - List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs(); - assert(sheetRuns != null); + List<List<HSLFTextParagraph>> runsV = new ArrayList<List<HSLFTextParagraph>>(); + for (EscherTextboxWrapper wrapper : ppdrawing.getTextboxWrappers()) { + runsV.add(findTextParagraphs(wrapper, sheet)); + } + return runsV; + } - for (List<HSLFTextParagraph> paras : sheetRuns) { + /** + * Scans through the supplied record array, looking for + * a TextHeaderAtom followed by one of a TextBytesAtom or + * a TextCharsAtom. Builds up TextRuns from these + * + * @param wrapper an EscherTextboxWrapper + */ + protected static List<HSLFTextParagraph> findTextParagraphs(EscherTextboxWrapper wrapper, HSLFSheet sheet) { + // propagate parents to parent-aware records + RecordContainer.handleParentAwareRecords(wrapper); + int shapeId = wrapper.getShapeId(); + List<HSLFTextParagraph> rv = null; + + OutlineTextRefAtom ota = (OutlineTextRefAtom)wrapper.findFirstOfType(OutlineTextRefAtom.typeID); + if (ota != null) { + // if we are based on an outline, there are no further records to be parsed from the wrapper + if (sheet == null) { + throw new RuntimeException("Outline atom reference can't be solved without a sheet record"); + } + + List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs(); + assert (sheetRuns != null); + + int idx = ota.getTextIndex(); + for (List<HSLFTextParagraph> r : sheetRuns) { + if (r.isEmpty()) continue; + int ridx = r.get(0).getIndex(); + if (ridx > idx) break; + if (ridx == idx) { + if (rv == null) { + rv = r; + } else { + // create a new container + // TODO: ... is this case really happening? + rv = new ArrayList<HSLFTextParagraph>(rv); + rv.addAll(r); + } + } + } + if (rv == null || rv.isEmpty()) { + logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx); + } + } else { + if (sheet != null) { + // check sheet runs first, so we get exactly the same paragraph list + List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs(); + assert (sheetRuns != null); + + for (List<HSLFTextParagraph> paras : sheetRuns) { if (!paras.isEmpty() && paras.get(0)._headerAtom.getParentRecord() == wrapper) { - rv = paras; - break; - } - } - } - - if (rv == null) { - // if we haven't found the wrapper in the sheet runs, create a new paragraph list from its record - List<List<HSLFTextParagraph>> rvl = findTextParagraphs(wrapper.getChildRecords()); - switch (rvl.size()) { - case 0: break; // nothing found - case 1: rv = rvl.get(0); break; // normal case - default: - throw new RuntimeException("TextBox contains more than one list of paragraphs."); - } - } - } - - if (rv != null) { - StyleTextProp9Atom styleTextProp9Atom = wrapper.getStyleTextProp9Atom(); - - for (HSLFTextParagraph htp : rv) { - htp.setShapeId(shapeId); - htp.setStyleTextProp9Atom(styleTextProp9Atom); - } - } - return rv; - } - - /** - * Scans through the supplied record array, looking for - * a TextHeaderAtom followed by one of a TextBytesAtom or - * a TextCharsAtom. Builds up TextRuns from these - * - * @param records the records to build from - */ + rv = paras; + break; + } + } + } + + if (rv == null) { + // if we haven't found the wrapper in the sheet runs, create a new paragraph list from its record + List<List<HSLFTextParagraph>> rvl = findTextParagraphs(wrapper.getChildRecords()); + switch (rvl.size()) { + case 0: break; // nothing found + case 1: rv = rvl.get(0); break; // normal case + default: + throw new RuntimeException("TextBox contains more than one list of paragraphs."); + } + } + } + + if (rv != null) { + StyleTextProp9Atom styleTextProp9Atom = wrapper.getStyleTextProp9Atom(); + + for (HSLFTextParagraph htp : rv) { + htp.setShapeId(shapeId); + htp.setStyleTextProp9Atom(styleTextProp9Atom); + } + } + return rv; + } + + /** + * Scans through the supplied record array, looking for + * a TextHeaderAtom followed by one of a TextBytesAtom or + * a TextCharsAtom. Builds up TextRuns from these + * + * @param records the records to build from + */ protected static List<List<HSLFTextParagraph>> findTextParagraphs(Record[] records) { List<List<HSLFTextParagraph>> paragraphCollection = new ArrayList<List<HSLFTextParagraph>>(); - int[] recordIdx = {0}; - + int[] recordIdx = { 0 }; + for (int slwtIndex = 0; recordIdx[0] < records.length; slwtIndex++) { - TextHeaderAtom header = null; - TextBytesAtom tbytes = null; - TextCharsAtom tchars = null; - TextRulerAtom ruler = null; + TextHeaderAtom header = null; + TextBytesAtom tbytes = null; + TextCharsAtom tchars = null; + TextRulerAtom ruler = null; MasterTextPropAtom indents = null; for (Record r : getRecords(records, recordIdx, null)) { long rt = r.getRecordType(); if (RecordTypes.TextHeaderAtom.typeID == rt) { - header = (TextHeaderAtom)r; + header = (TextHeaderAtom) r; } else if (RecordTypes.TextBytesAtom.typeID == rt) { - tbytes = (TextBytesAtom)r; + tbytes = (TextBytesAtom) r; } else if (RecordTypes.TextCharsAtom.typeID == rt) { - tchars = (TextCharsAtom)r; + tchars = (TextCharsAtom) r; } else if (RecordTypes.TextRulerAtom.typeID == rt) { - ruler = (TextRulerAtom)r; + ruler = (TextRulerAtom) r; } else if (RecordTypes.MasterTextPropAtom.typeID == rt) { - indents = (MasterTextPropAtom)r; + indents = (MasterTextPropAtom) r; } // don't search for RecordTypes.StyleTextPropAtom.typeID here ... see findStyleAtomPresent below } if (header == null) break; - + if (header.getParentRecord() instanceof SlideListWithText) { // runs found in PPDrawing are not linked with SlideListWithTexts header.setIndex(slwtIndex); @@ -1093,7 +1071,7 @@ public final class HSLFTextParagraph imp List<HSLFTextParagraph> paragraphs = new ArrayList<HSLFTextParagraph>(); paragraphCollection.add(paragraphs); - + // split, but keep delimiter for (String para : rawText.split("(?<=\r)")) { HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars); @@ -1116,7 +1094,7 @@ public final class HSLFTextParagraph imp if (paragraphCollection.isEmpty()) { logger.log(POILogger.DEBUG, "No text records found."); } - + return paragraphCollection; } @@ -1124,25 +1102,25 @@ public final class HSLFTextParagraph imp int paraIdx = 0, runIdx = 0; HSLFTextRun trun; - for (int csIdx=0; csIdx<charStyles.size(); csIdx++) { + for (int csIdx = 0; csIdx < charStyles.size(); csIdx++) { TextPropCollection p = charStyles.get(csIdx); - for (int ccRun = 0, ccStyle = p.getCharactersCovered(); ccRun < ccStyle; ) { + for (int ccRun = 0, ccStyle = p.getCharactersCovered(); ccRun < ccStyle;) { HSLFTextParagraph para = paragraphs.get(paraIdx); List<HSLFTextRun> runs = para.getTextRuns(); trun = runs.get(runIdx); int len = trun.getLength(); - if (ccRun+len <= ccStyle) { + if (ccRun + len <= ccStyle) { ccRun += len; } else { String text = trun.getRawText(); - trun.setText(text.substring(0,ccStyle-ccRun)); + trun.setText(text.substring(0, ccStyle - ccRun)); HSLFTextRun nextRun = new HSLFTextRun(para); - nextRun.setText(text.substring(ccStyle-ccRun)); - runs.add(runIdx+1, nextRun); + nextRun.setText(text.substring(ccStyle - ccRun)); + runs.add(runIdx + 1, nextRun); - ccRun += ccStyle-ccRun; + ccRun += ccStyle - ccRun; } TextPropCollection pCopy = new TextPropCollection(0, TextPropType.character); @@ -1151,7 +1129,7 @@ public final class HSLFTextParagraph imp len = trun.getLength(); if (paraIdx == paragraphs.size()-1 && runIdx == runs.size()-1) { - if (csIdx < charStyles.size()-1) { + if (csIdx < charStyles.size() - 1) { // special case, empty trailing text run HSLFTextRun nextRun = new HSLFTextRun(para); nextRun.setText(""); @@ -1204,7 +1182,7 @@ public final class HSLFTextParagraph imp len += trun.getLength(); } para.setIndentLevel(p.getIndentLevel()); - ccPara += len+1; + ccPara += len + 1; } } } @@ -1237,6 +1215,6 @@ public final class HSLFTextParagraph imp } public EscherTextboxWrapper getTextboxWrapper() { - return (EscherTextboxWrapper)_headerAtom.getParentRecord(); + return (EscherTextboxWrapper) _headerAtom.getParentRecord(); } }
Modified: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java?rev=1686117&r1=1686116&r2=1686117&view=diff ============================================================================== --- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java (original) +++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java Wed Jun 17 22:21:13 2015 @@ -17,7 +17,8 @@ package org.apache.poi.hslf.usermodel; -import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.fetchOrAddTextProp; +import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.setPropVal; +import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.getPropVal; import java.awt.Color; @@ -132,39 +133,17 @@ public final class HSLFTextRun implement * it if required. */ private void setCharFlagsTextPropVal(int index, boolean value) { - if(getFlag(index) != value) setFlag(index, value); + // TODO: check if paragraph/chars can be handled the same ... + if (getFlag(index) != value) setFlag(index, value); } /** - * Fetch the value of the given Character related TextProp. - * Returns -1 if that TextProp isn't present. - * If the TextProp isn't present, the value from the appropriate - * Master Sheet will apply. - */ - private int getCharTextPropVal(String propName) { - TextProp prop = null; - if (characterStyle != null){ - prop = characterStyle.findByName(propName); - } - - if (prop == null){ - HSLFSheet sheet = parentParagraph.getSheet(); - int txtype = parentParagraph.getRunType(); - HSLFMasterSheet master = sheet.getMasterSheet(); - if (master != null) - prop = master.getStyleAttribute(txtype, parentParagraph.getIndentLevel(), propName, true); - } - return prop == null ? -1 : prop.getValue(); - } - - /** * Sets the value of the given Paragraph TextProp, add if required * @param propName The name of the Paragraph TextProp * @param val The value to set for the TextProp */ public void setCharTextPropVal(String propName, int val) { - TextProp tp = fetchOrAddTextProp(characterStyle, propName); - tp.setValue(val); + setPropVal(characterStyle, propName, val); } @@ -260,8 +239,8 @@ public final class HSLFTextRun implement * @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript */ public int getSuperscript() { - int val = getCharTextPropVal("superscript"); - return val == -1 ? 0 : val; + TextProp tp = getPropVal(characterStyle, "superscript", parentParagraph); + return tp == null ? 0 : tp.getValue(); } /** @@ -270,14 +249,15 @@ public final class HSLFTextRun implement * @param val the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript */ public void setSuperscript(int val) { - setCharTextPropVal("superscript", val); + setPropVal(characterStyle, "superscript", val); } /** * Gets the font size */ - public double getFontSize() { - return getCharTextPropVal("font.size"); + public Double getFontSize() { + TextProp tp = getPropVal(characterStyle, "font.size", parentParagraph); + return tp == null ? null : (double)tp.getValue(); } @@ -292,7 +272,8 @@ public final class HSLFTextRun implement * Gets the font index */ public int getFontIndex() { - return getCharTextPropVal("font.index"); + TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph); + return tp == null ? -1 : tp.getValue(); } /** @@ -329,9 +310,9 @@ public final class HSLFTextRun implement if (sheet == null || slideShow == null) { return _fontFamily; } - int fontIdx = getCharTextPropVal("font.index"); - if(fontIdx == -1) { return null; } - return slideShow.getFontCollection().getFontWithId(fontIdx); + TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph); + if (tp == null) { return null; } + return slideShow.getFontCollection().getFontWithId(tp.getValue()); } /** @@ -339,7 +320,9 @@ public final class HSLFTextRun implement * @see java.awt.Color */ public Color getFontColor() { - int rgb = getCharTextPropVal("font.color"); + TextProp tp = getPropVal(characterStyle, "font.color", parentParagraph); + if (tp == null) return null; + int rgb = tp.getValue(); int cidx = rgb >> 24; if (rgb % 0x1000000 == 0){ @@ -370,7 +353,7 @@ public final class HSLFTextRun implement } protected void setFlag(int index, boolean value) { - BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(characterStyle, CharFlagsTextProp.NAME); + BitMaskTextProp prop = (BitMaskTextProp)characterStyle.addWithName(CharFlagsTextProp.NAME); prop.setSubValue(value, index); } Modified: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java?rev=1686117&r1=1686116&r2=1686117&view=diff ============================================================================== --- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java (original) +++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java Wed Jun 17 22:21:13 2015 @@ -55,12 +55,21 @@ public class DrawTextParagraph<T extends double rightInset = insets.right; double penY = y; - double leftMargin = paragraph.getLeftMargin(); boolean firstLine = true; - double indent = paragraph.getIndent(); + Double leftMargin = paragraph.getLeftMargin(); + Double indent = paragraph.getIndent(); + if (leftMargin == null) { + leftMargin = (indent != null) ? -indent : 0; + } + if (indent == null) { + indent = (leftMargin != null) ? -leftMargin : 0; + } + //The vertical line spacing - double spacing = paragraph.getLineSpacing(); + Double spacing = paragraph.getLineSpacing(); + if (spacing == null) spacing = 100d; + for(DrawTextFragment line : lines){ double penX = x + leftMargin; @@ -77,7 +86,7 @@ public class DrawTextParagraph<T extends bullet.setPosition(penX + indent, penY); } else if(indent > 0){ // a positive value means the "First Line" indentation: - // the first line is indented and other lines start at the bullet ofset + // the first line is indented and other lines start at the bullet offset bullet.setPosition(penX, penY); penX += indent; } else { @@ -96,7 +105,9 @@ public class DrawTextParagraph<T extends Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape()); - switch (paragraph.getTextAlign()) { + TextAlign ta = paragraph.getTextAlign(); + if (ta == null) ta = TextAlign.LEFT; + switch (ta) { case CENTER: penX += (anchor.getWidth() - leftMargin - line.getWidth() - leftInset - rightInset) / 2; break; @@ -219,9 +230,10 @@ public class DrawTextParagraph<T extends if (buColor == null) buColor = (Color)firstLineAttr.getAttribute(TextAttribute.FOREGROUND); float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE); - float buSz = (float)bulletStyle.getBulletFontSize(); - if(buSz > 0) fontSize *= buSz* 0.01; - else fontSize = -buSz; + Double buSz = bulletStyle.getBulletFontSize(); + if (buSz == null) buSz = 100d; + if (buSz > 0) fontSize *= buSz* 0.01; + else fontSize = (float)-buSz; AttributedString str = new AttributedString(buCharacter); @@ -237,10 +249,13 @@ public class DrawTextParagraph<T extends protected String getRenderableText(TextRun tr) { StringBuilder buf = new StringBuilder(); TextCap cap = tr.getTextCap(); + String tabs = null; for (char c : tr.getRawText().toCharArray()) { if(c == '\t') { - // TODO: finish support for tabs - buf.append(" "); + if (tabs == null) { + tabs = tab2space(tr); + } + buf.append(tabs); continue; } @@ -255,6 +270,34 @@ public class DrawTextParagraph<T extends return buf.toString(); } + + /** + * Replace a tab with the effective number of white spaces. + */ + private String tab2space(TextRun tr) { + AttributedString string = new AttributedString(" "); + String typeFace = tr.getFontFamily(); + if (typeFace == null) typeFace = "Lucida Sans"; + string.addAttribute(TextAttribute.FAMILY, typeFace); + + Double fs = tr.getFontSize(); + if (fs == null) fs = 12d; + string.addAttribute(TextAttribute.SIZE, fs.floatValue()); + + TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true)); + double wspace = l.getAdvance(); + + Double tabSz = paragraph.getDefaultTabSize(); + if (tabSz == null) tabSz = wspace*4; + + int numSpaces = (int)Math.ceil(tabSz / wspace); + StringBuilder buf = new StringBuilder(); + for(int i = 0; i < numSpaces; i++) { + buf.append(' '); + } + return buf.toString(); + } + /** * Returns wrapping width to break lines in this paragraph @@ -271,8 +314,14 @@ public class DrawTextParagraph<T extends Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape()); - double leftMargin = paragraph.getLeftMargin(); - double indent = paragraph.getIndent(); + Double leftMargin = paragraph.getLeftMargin(); + Double indent = paragraph.getIndent(); + if (leftMargin == null) { + leftMargin = (indent != null) ? -indent : 0; + } + if (indent == null) { + indent = (leftMargin != null) ? -leftMargin : 0; + } double width; TextShape<? extends TextParagraph<T>> ts = paragraph.getParentShape(); @@ -323,7 +372,9 @@ public class DrawTextParagraph<T extends text.append(runText); int endIndex = text.length(); - attList.add(new AttributedStringData(TextAttribute.FOREGROUND, run.getFontColor(), beginIndex, endIndex)); + Color fgColor = run.getFontColor(); + if (fgColor == null) fgColor = Color.BLACK; + attList.add(new AttributedStringData(TextAttribute.FOREGROUND, fgColor, beginIndex, endIndex)); // user can pass an custom object to convert fonts String fontFamily = run.getFontFamily(); @@ -335,10 +386,14 @@ public class DrawTextParagraph<T extends if(fontHandler != null) { fontFamily = fontHandler.getRendererableFont(fontFamily, run.getPitchAndFamily()); } + if (fontFamily == null) { + fontFamily = paragraph.getDefaultFontFamily(); + } attList.add(new AttributedStringData(TextAttribute.FAMILY, fontFamily, beginIndex, endIndex)); - float fontSz = (float)run.getFontSize(); - attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz, beginIndex, endIndex)); + Double fontSz = run.getFontSize(); + if (fontSz == null) fontSz = paragraph.getDefaultFontSize(); + attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), beginIndex, endIndex)); if(run.isBold()) { attList.add(new AttributedStringData(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, beginIndex, endIndex)); @@ -364,9 +419,9 @@ public class DrawTextParagraph<T extends // ensure that the paragraph contains at least one character // We need this trick to correctly measure text if (text.length() == 0) { - float fontSz = (float)paragraph.getDefaultFontSize(); + Double fontSz = paragraph.getDefaultFontSize(); text.append(" "); - attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz, 0, 1)); + attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), 0, 1)); } AttributedString string = new AttributedString(text.toString()); Modified: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java?rev=1686117&r1=1686116&r2=1686117&view=diff ============================================================================== --- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java (original) +++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java Wed Jun 17 22:21:13 2015 @@ -95,7 +95,8 @@ public class DrawTextShape<T extends Tex if (!isFirstLine) { // the amount of vertical white space before the paragraph - double spaceBefore = p.getSpaceBefore(); + Double spaceBefore = p.getSpaceBefore(); + if (spaceBefore == null) spaceBefore = 0d; if(spaceBefore > 0) { // positive value means percentage spacing of the height of the first line, e.g. // the higher the first line, the bigger the space before the paragraph @@ -112,7 +113,8 @@ public class DrawTextShape<T extends Tex y += dp.getY(); if (paragraphs.hasNext()) { - double spaceAfter = p.getSpaceAfter(); + Double spaceAfter = p.getSpaceAfter(); + if (spaceAfter == null) spaceAfter = 0d; if(spaceAfter > 0) { // positive value means percentage spacing of the height of the last line, e.g. // the higher the last line, the bigger the space after the paragraph --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
