602 lines
22 KiB
Java
602 lines
22 KiB
Java
/* ====================================================================
|
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
|
contributor license agreements. See the NOTICE file distributed with
|
|
this work for additional information regarding copyright ownership.
|
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
|
(the "License"); you may not use this file except in compliance with
|
|
the License. You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
==================================================================== */
|
|
|
|
package org.apache.poi.xssf.usermodel;
|
|
|
|
import java.util.*;
|
|
import java.util.regex.Pattern;
|
|
import java.util.regex.Matcher;
|
|
|
|
import javax.xml.namespace.QName;
|
|
|
|
import org.apache.poi.ss.usermodel.Font;
|
|
import org.apache.poi.ss.usermodel.RichTextString;
|
|
import org.apache.poi.xssf.model.StylesTable;
|
|
import org.apache.poi.xssf.model.ThemesTable;
|
|
import org.apache.poi.util.Internal;
|
|
import org.apache.xmlbeans.XmlCursor;
|
|
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor;
|
|
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFont;
|
|
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRElt;
|
|
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRPrElt;
|
|
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst;
|
|
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXstring;
|
|
|
|
|
|
/**
|
|
* Rich text unicode string. These strings can have fonts applied to arbitary parts of the string.
|
|
*
|
|
* <p>
|
|
* Most strings in a workbook have formatting applied at the cell level, that is, the entire string in the cell has the
|
|
* same formatting applied. In these cases, the formatting for the cell is stored in the styles part,
|
|
* and the string for the cell can be shared across the workbook. The following code illustrates the example.
|
|
* </p>
|
|
*
|
|
* <blockquote>
|
|
* <pre>
|
|
* cell1.setCellValue(new XSSFRichTextString("Apache POI"));
|
|
* cell2.setCellValue(new XSSFRichTextString("Apache POI"));
|
|
* cell3.setCellValue(new XSSFRichTextString("Apache POI"));
|
|
* </pre>
|
|
* </blockquote>
|
|
* In the above example all three cells will use the same string cached on workbook level.
|
|
*
|
|
* <p>
|
|
* Some strings in the workbook may have formatting applied at a level that is more granular than the cell level.
|
|
* For instance, specific characters within the string may be bolded, have coloring, italicizing, etc.
|
|
* In these cases, the formatting is stored along with the text in the string table, and is treated as
|
|
* a unique entry in the workbook. The following xml and code snippet illustrate this.
|
|
* </p>
|
|
*
|
|
* <blockquote>
|
|
* <pre>
|
|
* XSSFRichTextString s1 = new XSSFRichTextString("Apache POI");
|
|
* s1.applyFont(boldArial);
|
|
* cell1.setCellValue(s1);
|
|
*
|
|
* XSSFRichTextString s2 = new XSSFRichTextString("Apache POI");
|
|
* s2.applyFont(italicCourier);
|
|
* cell2.setCellValue(s2);
|
|
* </pre>
|
|
* </blockquote>
|
|
*/
|
|
public class XSSFRichTextString implements RichTextString {
|
|
private static final Pattern utfPtrn = Pattern.compile("_x([0-9A-Fa-f]{4})_");
|
|
|
|
private CTRst st;
|
|
private StylesTable styles;
|
|
|
|
/**
|
|
* Create a rich text string
|
|
*/
|
|
public XSSFRichTextString(String str) {
|
|
st = CTRst.Factory.newInstance();
|
|
st.setT(str);
|
|
preserveSpaces(st.xgetT());
|
|
}
|
|
|
|
/**
|
|
* Create empty rich text string and initialize it with empty string
|
|
*/
|
|
public XSSFRichTextString() {
|
|
st = CTRst.Factory.newInstance();
|
|
}
|
|
|
|
/**
|
|
* Create a rich text string from the supplied XML bean
|
|
*/
|
|
public XSSFRichTextString(CTRst st) {
|
|
this.st = st;
|
|
}
|
|
|
|
/**
|
|
* Applies a font to the specified characters of a string.
|
|
*
|
|
* @param startIndex The start index to apply the font to (inclusive)
|
|
* @param endIndex The end index to apply the font to (exclusive)
|
|
* @param fontIndex The font to use.
|
|
*/
|
|
public void applyFont(int startIndex, int endIndex, short fontIndex) {
|
|
XSSFFont font;
|
|
if(styles == null) {
|
|
//style table is not set, remember fontIndex and set the run properties later,
|
|
//when setStylesTableReference is called
|
|
font = new XSSFFont();
|
|
font.setFontName("#" + fontIndex);
|
|
} else {
|
|
font = styles.getFontAt(fontIndex);
|
|
}
|
|
applyFont(startIndex, endIndex, font);
|
|
}
|
|
|
|
/**
|
|
* Applies a font to the specified characters of a string.
|
|
*
|
|
* @param startIndex The start index to apply the font to (inclusive)
|
|
* @param endIndex The end index to apply to font to (exclusive)
|
|
* @param font The index of the font to use.
|
|
*/
|
|
public void applyFont(int startIndex, int endIndex, Font font) {
|
|
if (startIndex > endIndex)
|
|
throw new IllegalArgumentException("Start index must be less than end index, but had " + startIndex + " and " + endIndex);
|
|
if (startIndex < 0 || endIndex > length())
|
|
throw new IllegalArgumentException("Start and end index not in range, but had " + startIndex + " and " + endIndex);
|
|
|
|
if (startIndex == endIndex)
|
|
return;
|
|
|
|
if(st.sizeOfRArray() == 0 && st.isSetT()) {
|
|
//convert <t>string</t> into a text run: <r><t>string</t></r>
|
|
st.addNewR().setT(st.getT());
|
|
st.unsetT();
|
|
}
|
|
|
|
String text = getString();
|
|
XSSFFont xssfFont = (XSSFFont)font;
|
|
|
|
TreeMap<Integer, CTRPrElt> formats = getFormatMap(st);
|
|
CTRPrElt fmt = CTRPrElt.Factory.newInstance();
|
|
setRunAttributes(xssfFont.getCTFont(), fmt);
|
|
applyFont(formats, startIndex, endIndex, fmt);
|
|
|
|
CTRst newSt = buildCTRst(text, formats);
|
|
st.set(newSt);
|
|
}
|
|
|
|
/**
|
|
* Sets the font of the entire string.
|
|
* @param font The font to use.
|
|
*/
|
|
public void applyFont(Font font) {
|
|
String text = getString();
|
|
applyFont(0, text.length(), font);
|
|
}
|
|
|
|
/**
|
|
* Applies the specified font to the entire string.
|
|
*
|
|
* @param fontIndex the font to apply.
|
|
*/
|
|
public void applyFont(short fontIndex) {
|
|
XSSFFont font;
|
|
if(styles == null) {
|
|
font = new XSSFFont();
|
|
font.setFontName("#" + fontIndex);
|
|
} else {
|
|
font = styles.getFontAt(fontIndex);
|
|
}
|
|
String text = getString();
|
|
applyFont(0, text.length(), font);
|
|
}
|
|
|
|
/**
|
|
* Append new text to this text run and apply the specify font to it
|
|
*
|
|
* @param text the text to append
|
|
* @param font the font to apply to the appended text or <code>null</code> if no formatting is required
|
|
*/
|
|
public void append(String text, XSSFFont font){
|
|
if(st.sizeOfRArray() == 0 && st.isSetT()) {
|
|
//convert <t>string</t> into a text run: <r><t>string</t></r>
|
|
CTRElt lt = st.addNewR();
|
|
lt.setT(st.getT());
|
|
preserveSpaces(lt.xgetT());
|
|
st.unsetT();
|
|
}
|
|
CTRElt lt = st.addNewR();
|
|
lt.setT(text);
|
|
preserveSpaces(lt.xgetT());
|
|
|
|
if (font != null) {
|
|
CTRPrElt pr = lt.addNewRPr();
|
|
setRunAttributes(font.getCTFont(), pr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Append new text to this text run
|
|
*
|
|
* @param text the text to append
|
|
*/
|
|
public void append(String text){
|
|
append(text, null);
|
|
}
|
|
|
|
/**
|
|
* Copy font attributes from CTFont bean into CTRPrElt bean
|
|
*/
|
|
private void setRunAttributes(CTFont ctFont, CTRPrElt pr){
|
|
if(ctFont.sizeOfBArray() > 0) pr.addNewB().setVal(ctFont.getBArray(0).getVal());
|
|
if(ctFont.sizeOfUArray() > 0) pr.addNewU().setVal(ctFont.getUArray(0).getVal());
|
|
if(ctFont.sizeOfIArray() > 0) pr.addNewI().setVal(ctFont.getIArray(0).getVal());
|
|
if(ctFont.sizeOfColorArray() > 0) {
|
|
CTColor c1 = ctFont.getColorArray(0);
|
|
CTColor c2 = pr.addNewColor();
|
|
if(c1.isSetAuto()) c2.setAuto(c1.getAuto());
|
|
if(c1.isSetIndexed()) c2.setIndexed(c1.getIndexed());
|
|
if(c1.isSetRgb()) c2.setRgb(c1.getRgb());
|
|
if(c1.isSetTheme()) c2.setTheme(c1.getTheme());
|
|
if(c1.isSetTint()) c2.setTint(c1.getTint());
|
|
}
|
|
if(ctFont.sizeOfSzArray() > 0) pr.addNewSz().setVal(ctFont.getSzArray(0).getVal());
|
|
if(ctFont.sizeOfNameArray() > 0) pr.addNewRFont().setVal(ctFont.getNameArray(0).getVal());
|
|
if(ctFont.sizeOfFamilyArray() > 0) pr.addNewFamily().setVal(ctFont.getFamilyArray(0).getVal());
|
|
if(ctFont.sizeOfSchemeArray() > 0) pr.addNewScheme().setVal(ctFont.getSchemeArray(0).getVal());
|
|
if(ctFont.sizeOfCharsetArray() > 0) pr.addNewCharset().setVal(ctFont.getCharsetArray(0).getVal());
|
|
if(ctFont.sizeOfCondenseArray() > 0) pr.addNewCondense().setVal(ctFont.getCondenseArray(0).getVal());
|
|
if(ctFont.sizeOfExtendArray() > 0) pr.addNewExtend().setVal(ctFont.getExtendArray(0).getVal());
|
|
if(ctFont.sizeOfVertAlignArray() > 0) pr.addNewVertAlign().setVal(ctFont.getVertAlignArray(0).getVal());
|
|
if(ctFont.sizeOfOutlineArray() > 0) pr.addNewOutline().setVal(ctFont.getOutlineArray(0).getVal());
|
|
if(ctFont.sizeOfShadowArray() > 0) pr.addNewShadow().setVal(ctFont.getShadowArray(0).getVal());
|
|
if(ctFont.sizeOfStrikeArray() > 0) pr.addNewStrike().setVal(ctFont.getStrikeArray(0).getVal());
|
|
}
|
|
|
|
/**
|
|
* Does this string have any explicit formatting applied, or is
|
|
* it just text in the default style?
|
|
*/
|
|
public boolean hasFormatting() {
|
|
//noinspection deprecation - for performance reasons!
|
|
CTRElt[] rs = st.getRArray();
|
|
if (rs == null || rs.length == 0) {
|
|
return false;
|
|
}
|
|
for (CTRElt r : rs) {
|
|
if (r.isSetRPr()) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Removes any formatting that may have been applied to the string.
|
|
*/
|
|
public void clearFormatting() {
|
|
String text = getString();
|
|
st.setRArray(null);
|
|
st.setT(text);
|
|
}
|
|
|
|
/**
|
|
* The index within the string to which the specified formatting run applies.
|
|
*
|
|
* @param index the index of the formatting run
|
|
* @return the index within the string.
|
|
*/
|
|
public int getIndexOfFormattingRun(int index) {
|
|
if(st.sizeOfRArray() == 0) return 0;
|
|
|
|
int pos = 0;
|
|
for(int i = 0; i < st.sizeOfRArray(); i++){
|
|
CTRElt r = st.getRArray(i);
|
|
if(i == index) return pos;
|
|
|
|
pos += r.getT().length();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of characters this format run covers.
|
|
*
|
|
* @param index the index of the formatting run
|
|
* @return the number of characters this format run covers
|
|
*/
|
|
public int getLengthOfFormattingRun(int index) {
|
|
if(st.sizeOfRArray() == 0 || index >= st.sizeOfRArray()) {
|
|
return -1;
|
|
}
|
|
|
|
CTRElt r = st.getRArray(index);
|
|
return r.getT().length();
|
|
}
|
|
|
|
/**
|
|
* Returns the plain string representation.
|
|
*/
|
|
public String getString() {
|
|
if(st.sizeOfRArray() == 0) {
|
|
return utfDecode(st.getT());
|
|
}
|
|
StringBuilder buf = new StringBuilder();
|
|
//noinspection deprecation - for performance reasons!
|
|
for(CTRElt r : st.getRArray()){
|
|
buf.append(r.getT());
|
|
}
|
|
return utfDecode(buf.toString());
|
|
}
|
|
|
|
/**
|
|
* Removes any formatting and sets new string value
|
|
*
|
|
* @param s new string value
|
|
*/
|
|
public void setString(String s){
|
|
clearFormatting();
|
|
st.setT(s);
|
|
preserveSpaces(st.xgetT());
|
|
}
|
|
|
|
/**
|
|
* Returns the plain string representation.
|
|
*/
|
|
public String toString() {
|
|
return getString();
|
|
}
|
|
|
|
/**
|
|
* Returns the number of characters in this string.
|
|
*/
|
|
public int length() {
|
|
return getString().length();
|
|
}
|
|
|
|
/**
|
|
* @return The number of formatting runs used.
|
|
*/
|
|
public int numFormattingRuns() {
|
|
return st.sizeOfRArray();
|
|
}
|
|
|
|
/**
|
|
* Gets a copy of the font used in a particular formatting run.
|
|
*
|
|
* @param index the index of the formatting run
|
|
* @return A copy of the font used or null if no formatting is applied to the specified text run.
|
|
*/
|
|
public XSSFFont getFontOfFormattingRun(int index) {
|
|
if(st.sizeOfRArray() == 0 || index >= st.sizeOfRArray()) {
|
|
return null;
|
|
}
|
|
|
|
CTRElt r = st.getRArray(index);
|
|
if(r.getRPr() != null) {
|
|
XSSFFont fnt = new XSSFFont(toCTFont(r.getRPr()));
|
|
fnt.setThemesTable(getThemesTable());
|
|
return fnt;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Return a copy of the font in use at a particular index.
|
|
*
|
|
* @param index The index.
|
|
* @return A copy of the font that's currently being applied at that
|
|
* index or null if no font is being applied or the
|
|
* index is out of range.
|
|
*/
|
|
public XSSFFont getFontAtIndex( int index ) {
|
|
final ThemesTable themes = getThemesTable();
|
|
int pos = 0;
|
|
//noinspection deprecation - for performance reasons!
|
|
for(CTRElt r : st.getRArray()){
|
|
final int length = r.getT().length();
|
|
if(index >= pos && index < pos + length) {
|
|
XSSFFont fnt = new XSSFFont(toCTFont(r.getRPr()));
|
|
fnt.setThemesTable(themes);
|
|
return fnt;
|
|
}
|
|
|
|
pos += length;
|
|
}
|
|
return null;
|
|
|
|
}
|
|
|
|
/**
|
|
* Return the underlying xml bean
|
|
*/
|
|
@Internal
|
|
public CTRst getCTRst() {
|
|
return st;
|
|
}
|
|
|
|
protected void setStylesTableReference(StylesTable tbl){
|
|
styles = tbl;
|
|
if(st.sizeOfRArray() > 0) {
|
|
//noinspection deprecation - for performance reasons!
|
|
for (CTRElt r : st.getRArray()) {
|
|
CTRPrElt pr = r.getRPr();
|
|
if(pr != null && pr.sizeOfRFontArray() > 0){
|
|
String fontName = pr.getRFontArray(0).getVal();
|
|
if(fontName.startsWith("#")){
|
|
int idx = Integer.parseInt(fontName.substring(1));
|
|
XSSFFont font = styles.getFontAt(idx);
|
|
pr.removeRFont(0);
|
|
setRunAttributes(font.getCTFont(), pr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* CTRPrElt --> CTFont adapter
|
|
*/
|
|
protected static CTFont toCTFont(CTRPrElt pr){
|
|
CTFont ctFont = CTFont.Factory.newInstance();
|
|
|
|
// Bug 58315: there are files where there is no pr-entry for a RichTextString
|
|
if(pr == null) {
|
|
return ctFont;
|
|
}
|
|
|
|
if(pr.sizeOfBArray() > 0) ctFont.addNewB().setVal(pr.getBArray(0).getVal());
|
|
if(pr.sizeOfUArray() > 0) ctFont.addNewU().setVal(pr.getUArray(0).getVal());
|
|
if(pr.sizeOfIArray() > 0) ctFont.addNewI().setVal(pr.getIArray(0).getVal());
|
|
if(pr.sizeOfColorArray() > 0) {
|
|
CTColor c1 = pr.getColorArray(0);
|
|
CTColor c2 = ctFont.addNewColor();
|
|
if(c1.isSetAuto()) c2.setAuto(c1.getAuto());
|
|
if(c1.isSetIndexed()) c2.setIndexed(c1.getIndexed());
|
|
if(c1.isSetRgb()) c2.setRgb(c1.getRgb());
|
|
if(c1.isSetTheme()) c2.setTheme(c1.getTheme());
|
|
if(c1.isSetTint()) c2.setTint(c1.getTint());
|
|
}
|
|
if(pr.sizeOfSzArray() > 0) ctFont.addNewSz().setVal(pr.getSzArray(0).getVal());
|
|
if(pr.sizeOfRFontArray() > 0) ctFont.addNewName().setVal(pr.getRFontArray(0).getVal());
|
|
if(pr.sizeOfFamilyArray() > 0) ctFont.addNewFamily().setVal(pr.getFamilyArray(0).getVal());
|
|
if(pr.sizeOfSchemeArray() > 0) ctFont.addNewScheme().setVal(pr.getSchemeArray(0).getVal());
|
|
if(pr.sizeOfCharsetArray() > 0) ctFont.addNewCharset().setVal(pr.getCharsetArray(0).getVal());
|
|
if(pr.sizeOfCondenseArray() > 0) ctFont.addNewCondense().setVal(pr.getCondenseArray(0).getVal());
|
|
if(pr.sizeOfExtendArray() > 0) ctFont.addNewExtend().setVal(pr.getExtendArray(0).getVal());
|
|
if(pr.sizeOfVertAlignArray() > 0) ctFont.addNewVertAlign().setVal(pr.getVertAlignArray(0).getVal());
|
|
if(pr.sizeOfOutlineArray() > 0) ctFont.addNewOutline().setVal(pr.getOutlineArray(0).getVal());
|
|
if(pr.sizeOfShadowArray() > 0) ctFont.addNewShadow().setVal(pr.getShadowArray(0).getVal());
|
|
if(pr.sizeOfStrikeArray() > 0) ctFont.addNewStrike().setVal(pr.getStrikeArray(0).getVal());
|
|
|
|
return ctFont;
|
|
}
|
|
|
|
/**
|
|
* Add the xml:spaces="preserve" attribute if the string has leading or trailing spaces
|
|
*
|
|
* @param xs the string to check
|
|
*/
|
|
protected static void preserveSpaces(STXstring xs) {
|
|
String text = xs.getStringValue();
|
|
if (text != null && text.length() > 0) {
|
|
char firstChar = text.charAt(0);
|
|
char lastChar = text.charAt(text.length() - 1);
|
|
if(Character.isWhitespace(firstChar) || Character.isWhitespace(lastChar)) {
|
|
XmlCursor c = xs.newCursor();
|
|
c.toNextToken();
|
|
c.insertAttributeWithValue(new QName("http://www.w3.org/XML/1998/namespace", "space"), "preserve");
|
|
c.dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For all characters which cannot be represented in XML as defined by the XML 1.0 specification,
|
|
* the characters are escaped using the Unicode numerical character representation escape character
|
|
* format _xHHHH_, where H represents a hexadecimal character in the character's value.
|
|
* <p>
|
|
* Example: The Unicode character 0D is invalid in an XML 1.0 document,
|
|
* so it shall be escaped as <code>_x000D_</code>.
|
|
* </p>
|
|
* See section 3.18.9 in the OOXML spec.
|
|
*
|
|
* @param value the string to decode
|
|
* @return the decoded string
|
|
*/
|
|
static String utfDecode(String value){
|
|
if(value == null || !value.contains("_x")) {
|
|
return value;
|
|
}
|
|
|
|
StringBuilder buf = new StringBuilder();
|
|
Matcher m = utfPtrn.matcher(value);
|
|
int idx = 0;
|
|
while(m.find()) {
|
|
int pos = m.start();
|
|
if( pos > idx) {
|
|
buf.append(value.substring(idx, pos));
|
|
}
|
|
|
|
String code = m.group(1);
|
|
int icode = Integer.decode("0x" + code);
|
|
buf.append((char)icode);
|
|
|
|
idx = m.end();
|
|
}
|
|
|
|
// small optimization: don't go via StringBuilder if not necessary,
|
|
// the encodings are very rare, so we should almost always go via this shortcut.
|
|
if(idx == 0) {
|
|
return value;
|
|
}
|
|
|
|
buf.append(value.substring(idx));
|
|
return buf.toString();
|
|
}
|
|
|
|
void applyFont(TreeMap<Integer, CTRPrElt> formats, int startIndex, int endIndex, CTRPrElt fmt) {
|
|
// delete format runs that fit between startIndex and endIndex
|
|
// runs intersecting startIndex and endIndex remain
|
|
int runStartIdx = 0;
|
|
for (Iterator<Integer> it = formats.keySet().iterator(); it.hasNext();) {
|
|
int runEndIdx = it.next();
|
|
if (runStartIdx >= startIndex && runEndIdx < endIndex) {
|
|
it.remove();
|
|
}
|
|
runStartIdx = runEndIdx;
|
|
}
|
|
|
|
if(startIndex > 0 && !formats.containsKey(startIndex)) {
|
|
// If there's a format that starts later in the string, make it start now
|
|
for(Map.Entry<Integer, CTRPrElt> entry : formats.entrySet()) {
|
|
if(entry.getKey() > startIndex) {
|
|
formats.put(startIndex, entry.getValue());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
formats.put(endIndex, fmt);
|
|
|
|
// assure that the range [startIndex, endIndex] consists if a single run
|
|
// there can be two or three runs depending whether startIndex or endIndex
|
|
// intersected existing format runs
|
|
SortedMap<Integer, CTRPrElt> sub = formats.subMap(startIndex, endIndex);
|
|
while(sub.size() > 1) sub.remove(sub.lastKey());
|
|
}
|
|
|
|
TreeMap<Integer, CTRPrElt> getFormatMap(CTRst entry){
|
|
int length = 0;
|
|
TreeMap<Integer, CTRPrElt> formats = new TreeMap<Integer, CTRPrElt>();
|
|
//noinspection deprecation - for performance reasons!
|
|
for (CTRElt r : entry.getRArray()) {
|
|
String txt = r.getT();
|
|
CTRPrElt fmt = r.getRPr();
|
|
|
|
length += txt.length();
|
|
formats.put(length, fmt);
|
|
}
|
|
return formats;
|
|
}
|
|
|
|
CTRst buildCTRst(String text, TreeMap<Integer, CTRPrElt> formats){
|
|
if(text.length() != formats.lastKey()) {
|
|
throw new IllegalArgumentException("Text length was " + text.length() +
|
|
" but the last format index was " + formats.lastKey());
|
|
}
|
|
CTRst stf = CTRst.Factory.newInstance();
|
|
int runStartIdx = 0;
|
|
for (Map.Entry<Integer, CTRPrElt> me : formats.entrySet()) {
|
|
int runEndIdx = me.getKey();
|
|
CTRElt run = stf.addNewR();
|
|
String fragment = text.substring(runStartIdx, runEndIdx);
|
|
run.setT(fragment);
|
|
preserveSpaces(run.xgetT());
|
|
|
|
CTRPrElt fmt = me.getValue();
|
|
if (fmt != null) {
|
|
run.setRPr(fmt);
|
|
}
|
|
runStartIdx = runEndIdx;
|
|
}
|
|
return stf;
|
|
}
|
|
|
|
private ThemesTable getThemesTable() {
|
|
if(styles == null) return null;
|
|
return styles.getTheme();
|
|
}
|
|
}
|