fixed HSLF text handling

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/common_sl@1677496 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2015-05-03 23:42:42 +00:00
parent f0d95c3f8d
commit 02e70fdbfe
11 changed files with 252 additions and 162 deletions

View File

@ -490,27 +490,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
tabStops.addNewTab().setPos(Units.toEMU(value));
}
/**
* This element specifies the vertical line spacing that is to be used within a paragraph.
* This may be specified in two different ways, percentage spacing and font point spacing:
* <p>
* If linespacing >= 0, then linespacing is a percentage of normal line height
* If linespacing < 0, the absolute value of linespacing is the spacing in points
* </p>
* Examples:
* <pre><code>
* // spacing will be 120% of the size of the largest text on each line
* paragraph.setLineSpacing(120);
*
* // spacing will be 200% of the size of the largest text on each line
* paragraph.setLineSpacing(200);
*
* // spacing will be 48 points
* paragraph.setLineSpacing(-48.0);
* </code></pre>
*
* @param linespacing the vertical line spacing
*/
@Override
public void setLineSpacing(double linespacing){
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
@ -519,16 +499,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
pr.setLnSpc(spc);
}
/**
* Returns the vertical line spacing that is to be used within a paragraph.
* This may be specified in two different ways, percentage spacing and font point spacing:
* <p>
* If linespacing >= 0, then linespacing is a percentage of normal line height.
* If linespacing < 0, the absolute value of linespacing is the spacing in points
* </p>
*
* @return the vertical line spacing.
*/
@Override
public double getLineSpacing(){
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
public boolean fetch(CTTextParagraphProperties props){

View File

@ -82,13 +82,9 @@ public abstract class BitMaskTextProp extends TextProp implements Cloneable {
* Set the true/false status of the subproperty with the given index
*/
public void setSubValue(boolean value, int idx) {
if(subPropMatches[idx] == value) { return; }
if(value) {
dataValue += subPropMasks[idx];
} else {
dataValue -= subPropMasks[idx];
}
subPropMatches[idx] = value;
if (subPropMatches[idx] == value) return;
subPropMatches[idx] = value;
dataValue ^= subPropMasks[idx];
}
@Override
@ -101,4 +97,8 @@ public abstract class BitMaskTextProp extends TextProp implements Cloneable {
return newObj;
}
public BitMaskTextProp cloneAll(){
return (BitMaskTextProp)super.clone();
}
}

View File

@ -160,14 +160,9 @@ public class TextPropCollection {
this.reservedField = other.reservedField;
this.textPropList.clear();
for (TextProp tp : other.textPropList) {
TextProp tpCopy = tp.clone();
if (tpCopy instanceof BitMaskTextProp) {
BitMaskTextProp bmt = (BitMaskTextProp)tpCopy;
boolean matches[] = ((BitMaskTextProp)tp).getSubPropMatches();
for (int i=0; i<matches.length; i++) {
bmt.setSubValue(matches[i], i);
}
}
TextProp tpCopy = (tp instanceof BitMaskTextProp)
? ((BitMaskTextProp)tp).cloneAll()
: tp.clone();
this.textPropList.add(tpCopy);
}
}
@ -183,7 +178,7 @@ public class TextPropCollection {
/**
* Writes out to disk the header, and then all the properties
*/
public void writeOut(OutputStream o) throws IOException {
public void writeOut(OutputStream o, TextProp[] potentialProperties) throws IOException {
// First goes the number of characters we affect
StyleTextPropAtom.writeLittleEndian(charactersCovered,o);
@ -194,10 +189,8 @@ public class TextPropCollection {
// Then the mask field
int mask = maskSpecial;
for(int i=0; i<textPropList.size(); i++) {
TextProp textProp = textPropList.get(i);
for(TextProp textProp : textPropList) {
//sometimes header indicates that the bitmask is present but its value is 0
if (textProp instanceof BitMaskTextProp) {
if(mask == 0) mask |= textProp.getWriteMask();
}
@ -208,14 +201,19 @@ public class TextPropCollection {
StyleTextPropAtom.writeLittleEndian(mask,o);
// Then the contents of all the properties
for(int i=0; i<textPropList.size(); i++) {
TextProp textProp = textPropList.get(i);
int val = textProp.getValue();
if(textProp.getSize() == 2) {
StyleTextPropAtom.writeLittleEndian((short)val,o);
} else if(textProp.getSize() == 4){
StyleTextPropAtom.writeLittleEndian(val,o);
}
for (TextProp potProp : potentialProperties) {
for(TextProp textProp : textPropList) {
if (!textProp.getName().equals(potProp.getName())) continue;
int val = textProp.getValue();
if (textProp instanceof BitMaskTextProp && val == 0) {
// don't add empty properties, as they can't be recognized while reading
continue;
} else if (textProp.getSize() == 2) {
StyleTextPropAtom.writeLittleEndian((short)val,o);
} else if (textProp.getSize() == 4) {
StyleTextPropAtom.writeLittleEndian(val,o);
}
}
}
}

View File

@ -17,15 +17,12 @@
package org.apache.poi.hslf.record;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedList;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hslf.model.textproperties.*;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.*;
/**
* A StyleTextPropAtom (type 4001). Holds basic character properties
@ -64,26 +61,26 @@ public final class StyleTextPropAtom extends RecordAtom
* Characters the paragraph covers, and also contains the TextProps
* that actually define the styling of the paragraph.
*/
private LinkedList<TextPropCollection> paragraphStyles;
public LinkedList<TextPropCollection> getParagraphStyles() { return paragraphStyles; }
private List<TextPropCollection> paragraphStyles;
public List<TextPropCollection> getParagraphStyles() { return paragraphStyles; }
/**
* Updates the link list of TextPropCollections which make up the
* paragraph stylings
*/
public void setParagraphStyles(LinkedList<TextPropCollection> ps) { paragraphStyles = ps; }
public void setParagraphStyles(List<TextPropCollection> ps) { paragraphStyles = ps; }
/**
* The list of all the different character stylings we code for.
* Each entry is a TextPropCollection, which tells you how many
* Characters the character styling covers, and also contains the
* TextProps that actually define the styling of the characters.
*/
private LinkedList<TextPropCollection> charStyles;
public LinkedList<TextPropCollection> getCharacterStyles() { return charStyles; }
private List<TextPropCollection> charStyles;
public List<TextPropCollection> getCharacterStyles() { return charStyles; }
/**
* Updates the link list of TextPropCollections which make up the
* character stylings
*/
public void setCharacterStyles(LinkedList<TextPropCollection> cs) { charStyles = cs; }
public void setCharacterStyles(List<TextPropCollection> cs) { charStyles = cs; }
/**
* Returns how many characters the paragraph's
@ -105,7 +102,7 @@ public final class StyleTextPropAtom extends RecordAtom
public int getCharacterTextLengthCovered() {
return getCharactersCovered(charStyles);
}
private int getCharactersCovered(LinkedList<TextPropCollection> styles) {
private int getCharactersCovered(List<TextPropCollection> styles) {
int length = 0;
for(TextPropCollection tpc : styles) {
length += tpc.getCharactersCovered();
@ -197,9 +194,9 @@ public final class StyleTextPropAtom extends RecordAtom
System.arraycopy(source,start+8,rawContents,0,rawContents.length);
reserved = new byte[0];
// Set empty linked lists, ready for when they call setParentTextSize
paragraphStyles = new LinkedList<TextPropCollection>();
charStyles = new LinkedList<TextPropCollection>();
// Set empty lists, ready for when they call setParentTextSize
paragraphStyles = new ArrayList<TextPropCollection>();
charStyles = new ArrayList<TextPropCollection>();
}
@ -217,8 +214,8 @@ public final class StyleTextPropAtom extends RecordAtom
LittleEndian.putInt(_header,4,10);
// Set empty paragraph and character styles
paragraphStyles = new LinkedList<TextPropCollection>();
charStyles = new LinkedList<TextPropCollection>();
paragraphStyles = new ArrayList<TextPropCollection>();
charStyles = new ArrayList<TextPropCollection>();
TextPropCollection defaultParagraphTextProps =
new TextPropCollection(parentTextSize, (short)0);
@ -377,13 +374,13 @@ public final class StyleTextPropAtom extends RecordAtom
// First up, we need to serialise the paragraph properties
for(int i=0; i<paragraphStyles.size(); i++) {
TextPropCollection tpc = paragraphStyles.get(i);
tpc.writeOut(baos);
tpc.writeOut(baos, paragraphTextPropTypes);
}
// Now, we do the character ones
for(int i=0; i<charStyles.size(); i++) {
TextPropCollection tpc = charStyles.get(i);
tpc.writeOut(baos);
tpc.writeOut(baos, characterTextPropTypes);
}
rawContents = baos.toByteArray();
@ -454,7 +451,7 @@ public final class StyleTextPropAtom extends RecordAtom
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
pr.writeOut(baos);
pr.writeOut(baos, paragraphTextPropTypes);
byte[] b = baos.toByteArray();
out.append(HexDump.dump(b, 0, 0));
} catch (Exception e ) {
@ -475,7 +472,7 @@ public final class StyleTextPropAtom extends RecordAtom
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
pr.writeOut(baos);
pr.writeOut(baos, characterTextPropTypes);
byte[] b = baos.toByteArray();
out.append(HexDump.dump(b, 0, 0));
} catch (Exception e ) {

View File

@ -270,7 +270,6 @@ public abstract class HSLFSheet implements Sheet<HSLFShape,HSLFSlideShow> {
* @param shape
*/
protected void onAddTextShape(HSLFTextShape shape) {
}
/**

View File

@ -553,30 +553,22 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
return getParaTextPropVal("bullet.font");
}
/**
* Sets the line spacing.
* <p>
* If linespacing >= 0, then linespacing is a percentage of normal line height.
* If linespacing < 0, the absolute value of linespacing is the spacing in master coordinates.
* </p>
*/
public void setLineSpacing(int val) {
setParaTextPropVal("linespacing", val);
@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);
}
/**
* Returns the line spacing
* <p>
* If linespacing >= 0, then linespacing is a percentage of normal line height.
* If linespacing < 0, the absolute value of linespacing is the spacing in master coordinates.
* </p>
*
* @return the spacing between lines
*/
@Override
public double getLineSpacing() {
int val = getParaTextPropVal("linespacing");
return val == -1 ? 0 : val;
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;
}
/**
@ -722,6 +714,12 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
throw new RuntimeException("child record not found - malformed container record");
}
cr[idx] = newRecord;
if (newRecord == byteAtom) {
charAtom = null;
} else {
byteAtom = null;
}
}
// Ensure a StyleTextPropAtom is present, adding if required
@ -750,6 +748,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
}
for (HSLFTextRun tr : para.getTextRuns()) {
TextPropCollection rtpc = tr.getCharacterStyle();
rtpc.updateTextSize(0);
if (!rtpc.equals(lastRTPC)) {
lastRTPC = styleAtom.addCharacterTextPropCollection(0);
lastRTPC.copy(rtpc);
@ -783,19 +782,43 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
*
* @param text the text string used by this object.
*/
protected static void appendText(List<HSLFTextParagraph> paragraphs, String text, boolean newParagraph) {
protected static HSLFTextRun appendText(List<HSLFTextParagraph> paragraphs, String text, boolean newParagraph) {
text = toInternalString(text);
// init paragraphs
assert(!paragraphs.isEmpty());
// check paragraphs
assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());
HSLFTextParagraph lastHTP = paragraphs.get(paragraphs.size()-1);
HSLFTextRun lastHTR = lastHTP.getTextRuns().get(lastHTP.getTextRuns().size()-1);
HSLFTextParagraph htp = (newParagraph) ? new HSLFTextParagraph(lastHTP) : lastHTP;
HSLFTextRun htr = new HSLFTextRun(htp);
htr.setText(text);
htr.getCharacterStyle().copy(lastHTR.getCharacterStyle());
htp.addTextRun(htr);
HSLFTextParagraph htp = paragraphs.get(paragraphs.size()-1);
HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size()-1);
if (newParagraph) {
htr.setText(htr.getRawText()+"\n");
}
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._styleAtom);
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);
}
return htr;
}
/**
@ -804,29 +827,30 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
*
* @param text the text string used by this object.
*/
public static void setText(List<HSLFTextParagraph> paragraphs, String text) {
public static HSLFTextRun setText(List<HSLFTextParagraph> paragraphs, String text) {
text = HSLFTextParagraph.toInternalString(text);
// init paragraphs
assert(!paragraphs.isEmpty());
// check paragraphs
assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());
Iterator<HSLFTextParagraph> paraIter = paragraphs.iterator();
HSLFTextParagraph firstHTP = paraIter.next(); // keep first
assert(firstHTP != null);
HSLFTextParagraph htp = paraIter.next(); // keep first
assert(htp != null);
while (paraIter.hasNext()) {
paraIter.next();
paraIter.remove();
}
Iterator<HSLFTextRun> runIter = firstHTP.getTextRuns().iterator();
HSLFTextRun firstHTR = runIter.next();
assert(firstHTR != null);
Iterator<HSLFTextRun> runIter = htp.getTextRuns().iterator();
HSLFTextRun htr = runIter.next();
htr.setText("");
assert(htr != null);
while (runIter.hasNext()) {
runIter.next();
runIter.remove();
}
firstHTR.setText(text);
return appendText(paragraphs, text, false);
}
public static String getRawText(List<HSLFTextParagraph> paragraphs) {
@ -835,9 +859,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
for (HSLFTextRun r : p.getTextRuns()) {
sb.append(r.getRawText());
}
sb.append("\r");
}
sb.deleteCharAt(sb.length()-1); // remove last line break
return sb.toString();
}
@ -947,7 +969,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
return paragraphCollection;
}
for (int slwtIndex = 0; recordIdx < records.length-2; slwtIndex++) {
for (int slwtIndex = 0; recordIdx < records.length; slwtIndex++) {
List<HSLFTextParagraph> paragraphs = new ArrayList<HSLFTextParagraph>();
paragraphCollection.add(paragraphs);
@ -985,12 +1007,14 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
if (tbytes == null && tchars == null) {
tbytes = new TextBytesAtom();
header.getParentRecord().addChildAfter(tbytes, header);
logger.log(POILogger.INFO, "bytes nor chars atom doesn't exist. Creating dummy record for later saving.");
}
String rawText = (tchars != null) ? tchars.getText() : tbytes.getText();
for (String para : rawText.split("\r")) {
// split, but keep delimiter
for (String para : rawText.split("(?<=\r)")) {
HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars, styles);
paragraphs.add(tpara);
tpara.setStyleTextProp9Atom(styleTextProp9Atom);
@ -1021,29 +1045,21 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
protected static void applyCharacterStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) {
int paraIdx = 0, runIdx = 0;
for (TextPropCollection p : charStyles) {
HSLFTextRun trun;
for (int csIdx=0; csIdx<charStyles.size(); csIdx++) {
TextPropCollection p = charStyles.get(csIdx);
for (int ccRun = 0, ccStyle = p.getCharactersCovered(); ccRun < ccStyle; ) {
HSLFTextParagraph para = paragraphs.get(paraIdx);
List<HSLFTextRun> runs = para.getTextRuns();
HSLFTextRun trun = runs.get(runIdx);
trun = runs.get(runIdx);
int len = trun.getLength();
if (runIdx+1 >= runs.size()) {
// need to add +1 to the last run of the paragraph
len++;
}
TextPropCollection pCopy = new TextPropCollection(1);
pCopy.copy(p);
if (ccRun+len <= ccStyle) {
trun.setCharacterStyle(pCopy);
pCopy.updateTextSize(len);
ccRun += len;
} else {
String text = trun.getRawText();
trun.setText(text.substring(0,ccStyle-ccRun));
pCopy.updateTextSize(ccStyle-ccRun);
trun.setCharacterStyle(pCopy);
HSLFTextRun nextRun = new HSLFTextRun(para);
nextRun.setText(text.substring(ccStyle-ccRun));
@ -1052,8 +1068,27 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
ccRun += ccStyle-ccRun;
}
// need to compare it again, in case a run has been added afer
if (++runIdx >= runs.size()) {
TextPropCollection pCopy = new TextPropCollection(0);
pCopy.copy(p);
trun.setCharacterStyle(pCopy);
len = trun.getLength();
if (paraIdx == paragraphs.size()-1 && runIdx == runs.size()-1) {
if (csIdx < charStyles.size()-1) {
// special case, empty trailing text run
HSLFTextRun nextRun = new HSLFTextRun(para);
nextRun.setText("");
runs.add(nextRun);
} else {
// need to add +1 to the last run of the last paragraph
len++;
ccRun++;
}
}
pCopy.updateTextSize(len);
// need to compare it again, in case a run has been added after
if (++runIdx == runs.size()) {
paraIdx++;
runIdx = 0;
}
@ -1065,16 +1100,18 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
int paraIdx = 0;
for (TextPropCollection p : paraStyles) {
for (int ccPara = 0, ccStyle = p.getCharactersCovered(); ccPara < ccStyle; paraIdx++) {
HSLFTextParagraph para = paragraphs.get(paraIdx);
TextPropCollection pCopy = new TextPropCollection(1);
if (paraIdx >= paragraphs.size() || ccPara >= ccStyle-1) return;
HSLFTextParagraph htp = paragraphs.get(paraIdx);
TextPropCollection pCopy = new TextPropCollection(0);
pCopy.copy(p);
htp.setParagraphStyle(pCopy);
int len = 0;
for (HSLFTextRun trun : para.getTextRuns()) {
for (HSLFTextRun trun : htp.getTextRuns()) {
len += trun.getLength();
}
pCopy.updateTextSize(len+1);
para.setParagraphStyle(pCopy);
ccPara += len+1;
if (paraIdx == paragraphs.size()-1) len++;
pCopy.updateTextSize(len);
ccPara += len;
}
}
}
@ -1101,15 +1138,28 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
tha.setParentRecord(wrapper);
wrapper.appendChildRecord(tha);
TextCharsAtom tca = new TextCharsAtom();
wrapper.appendChildRecord(tca);
TextBytesAtom tba = new TextBytesAtom();
tba.setText("\r".getBytes());
wrapper.appendChildRecord(tba);
StyleTextPropAtom sta = new StyleTextPropAtom(0);
StyleTextPropAtom sta = new StyleTextPropAtom(1);
TextPropCollection paraStyle = sta.addParagraphTextPropCollection(1);
TextPropCollection charStyle = sta.addCharacterTextPropCollection(1);
wrapper.appendChildRecord(sta);
HSLFTextParagraph htp = new HSLFTextParagraph(tha, null, tca, sta);
HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, sta);
htp.setParagraphStyle(paraStyle);
htp._records = new Record[0];
htp.setBullet(false);
htp.setLineSpacing(100);
htp.setLeftMargin(0);
htp.setIndent(0);
// set wrap flags
HSLFTextRun htr = new HSLFTextRun(htp);
htr.setCharacterStyle(charStyle);
htr.setText("\r");
htr.setFontColor(Color.black);
htp.addTextRun(htr);
return Arrays.asList(htp);

View File

@ -37,14 +37,14 @@ public final class HSLFTextRun implements TextRun {
/** The TextRun we belong to */
private HSLFTextParagraph parentParagraph;
private String _runText = "\r";
private String _runText = "";
private String _fontname;
/**
* Our paragraph and character style.
* Note - we may share these styles with other RichTextRuns
*/
private TextPropCollection characterStyle = new TextPropCollection(1);
private TextPropCollection characterStyle = new TextPropCollection(0);
/**
* Create a new wrapper around a rich text string

View File

@ -22,14 +22,11 @@ import static org.apache.poi.hslf.record.RecordTypes.*;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Rectangle2D.Double;
import java.io.IOException;
import java.util.*;
import org.apache.poi.POIXMLException;
import org.apache.poi.ddf.*;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
import org.apache.poi.hslf.record.*;
import org.apache.poi.sl.draw.DrawFactory;
import org.apache.poi.sl.draw.DrawTextShape;
@ -139,8 +136,12 @@ public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape
protected void afterInsert(HSLFSheet sh){
super.afterInsert(sh);
storeText();
EscherTextboxWrapper _txtbox = getEscherTextboxWrapper();
if(_txtbox != null){
_escherContainer.addChildRecord(_txtbox.getEscherRecord());
PPDrawing ppdrawing = sh.getPPDrawing();
ppdrawing.addTextboxWrapper(_txtbox);
// Ensure the escher layer knows about the added records
@ -712,10 +713,10 @@ public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape
*
* @param text the text string used by this object.
*/
public void appendText(String text, boolean newParagraph) {
public HSLFTextRun appendText(String text, boolean newParagraph) {
// init paragraphs
List<HSLFTextParagraph> paras = getTextParagraphs();
HSLFTextParagraph.appendText(paras, text, newParagraph);
return HSLFTextParagraph.appendText(paras, text, newParagraph);
}
/**
@ -723,12 +724,15 @@ public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape
* Uses the properties of the first paragraph / textrun
*
* @param text the text string used by this object.
*
* @return the last text run of the splitted text
*/
public void setText(String text) {
public HSLFTextRun setText(String text) {
// init paragraphs
List<HSLFTextParagraph> paras = getTextParagraphs();
HSLFTextParagraph.setText(paras, text);
HSLFTextRun htr = HSLFTextParagraph.setText(paras, text);
setTextId(text.hashCode());
return htr;
}
/**

View File

@ -104,8 +104,8 @@ public class DrawTextShape<T extends TextShape<? extends TextParagraph<? extends
// negative value means the absolute spacing in points
y += -spaceBefore;
}
isFirstLine = false;
}
isFirstLine = false;
dp.setPosition(x, y);
dp.draw(graphics);

View File

@ -113,7 +113,7 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
* This may be specified in two different ways, percentage spacing and font point spacing:
* <p>
* If spaceBefore >= 0, then space is a percentage of normal line height.
* If spaceBefore < 0, the absolute value of linespacing is the spacing in points
* If spaceBefore < 0, the absolute value in points
* </p>
*
* @return the vertical white space before the paragraph
@ -175,6 +175,29 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
*/
double getLineSpacing();
/**
* This element specifies the vertical line spacing that is to be used within a paragraph.
* This may be specified in two different ways, percentage spacing and font point spacing:
* <p>
* If linespacing >= 0, then linespacing is a percentage of normal line height
* If linespacing < 0, the absolute value of linespacing is the spacing in points
* </p>
* Examples:
* <pre><code>
* // spacing will be 120% of the size of the largest text on each line
* paragraph.setLineSpacing(120);
*
* // spacing will be 200% of the size of the largest text on each line
* paragraph.setLineSpacing(200);
*
* // spacing will be 48 points
* paragraph.setLineSpacing(-48.0);
* </code></pre>
*
* @param linespacing the vertical line spacing
*/
void setLineSpacing(double lineSpacing);
String getDefaultFontFamily();
/**

View File

@ -20,6 +20,8 @@ package org.apache.poi.hslf.model;
import static org.junit.Assert.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Rectangle2D.Double;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
@ -136,6 +138,48 @@ public final class TestShapes {
}
}
@Test
public void testParagraphs() throws Exception {
HSLFSlideShow ppt = new HSLFSlideShow();
HSLFSlide slide = ppt.createSlide();
HSLFTextBox shape = new HSLFTextBox();
HSLFTextRun p1r1 = shape.setText("para 1 run 1. ");
HSLFTextRun p1r2 = shape.appendText("para 1 run 2.", false);
HSLFTextRun p2r1 = shape.appendText("para 2 run 1. ", true);
HSLFTextRun p2r2 = shape.appendText("para 2 run 2. ", false);
p1r1.setFontColor(Color.black);
p1r2.setFontColor(Color.red);
p2r1.setFontColor(Color.yellow);
p2r2.setStrikethrough(true);
// run 3 has same text properties as run 2 and will be merged when saving
HSLFTextRun p2r3 = shape.appendText("para 2 run 3.", false);
shape.setAnchor(new Rectangle2D.Double(100,100,100,10));
slide.addShape(shape);
shape.resizeToFitText();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ppt.write(bos);
ppt = new HSLFSlideShow(new ByteArrayInputStream(bos.toByteArray()));
slide = ppt.getSlides().get(0);
HSLFTextBox tb = (HSLFTextBox)slide.getShapes().get(0);
List<HSLFTextParagraph> para = tb.getTextParagraphs();
HSLFTextRun tr = para.get(0).getTextRuns().get(0);
assertEquals("para 1 run 1. ", tr.getRawText());
assertEquals(Color.black, tr.getFontColor());
tr = para.get(0).getTextRuns().get(1);
assertEquals("para 1 run 2.\r", tr.getRawText());
assertEquals(Color.red, tr.getFontColor());
tr = para.get(1).getTextRuns().get(0);
assertEquals("para 2 run 1. ", tr.getRawText());
assertEquals(Color.yellow, tr.getFontColor());
tr = para.get(1).getTextRuns().get(1);
assertEquals("para 2 run 2. para 2 run 3.", tr.getRawText());
assertEquals(Color.black, tr.getFontColor());
assertTrue(tr.isStrikethrough());
}
/**
* Verify that we can add TextBox shapes to a slide
* and set some of the style attributes
@ -235,7 +279,11 @@ public final class TestShapes {
for (HSLFShape sh : sld.getShapes()) {
if (sh instanceof HSLFTextShape){
HSLFTextShape tbox = (HSLFTextShape)sh;
lst2.add(tbox.getText());
for (HSLFTextParagraph p : tbox.getTextParagraphs()) {
for (HSLFTextRun r : p) {
lst2.add(r.getRawText());
}
}
}
}
assertTrue(lst1.containsAll(lst2));