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:
parent
f0d95c3f8d
commit
02e70fdbfe
@ -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){
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 ) {
|
||||
|
@ -270,7 +270,6 @@ public abstract class HSLFSheet implements Sheet<HSLFShape,HSLFSlideShow> {
|
||||
* @param shape
|
||||
*/
|
||||
protected void onAddTextShape(HSLFTextShape shape) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
/**
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user