339 lines
11 KiB
Java
339 lines
11 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 com.moparisthebest.poi.hssf.usermodel;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import com.moparisthebest.poi.hssf.model.InternalWorkbook;
|
|
import com.moparisthebest.poi.hssf.record.LabelSSTRecord;
|
|
import com.moparisthebest.poi.hssf.record.common.UnicodeString;
|
|
import com.moparisthebest.poi.hssf.record.common.UnicodeString.FormatRun;
|
|
import com.moparisthebest.poi.ss.usermodel.Font;
|
|
import com.moparisthebest.poi.ss.usermodel.RichTextString;
|
|
/**
|
|
* Rich text unicode string. These strings can have fonts applied to
|
|
* arbitary parts of the string.
|
|
*
|
|
* <p>
|
|
* Note, that in certain cases creating too many HSSFRichTextString cells may cause Excel 2003 and lower to crash
|
|
* when changing the color of the cells and then saving the Excel file. Compare two snippets that produce equivalent output:
|
|
*
|
|
* <p><blockquote><pre>
|
|
* HSSFCell hssfCell = row.createCell(idx);
|
|
* //rich text consists of two runs
|
|
* HSSFRichTextString richString = new HSSFRichTextString( "Hello, World!" );
|
|
* richString.applyFont( 0, 6, font1 );
|
|
* richString.applyFont( 6, 13, font2 );
|
|
* hssfCell.setCellValue( richString );
|
|
* </pre></blockquote>
|
|
*
|
|
* and
|
|
*
|
|
* <p><blockquote><pre>
|
|
* //create a cell style and assign the first font to it
|
|
* HSSFCellStyle style = workbook.createCellStyle();
|
|
* style.setFont(font1);
|
|
*
|
|
* HSSFCell hssfCell = row.createCell(idx);
|
|
* hssfCell.setCellStyle(style);
|
|
*
|
|
* //rich text consists of one run overriding the cell style
|
|
* HSSFRichTextString richString = new HSSFRichTextString( "Hello, World!" );
|
|
* richString.applyFont( 6, 13, font2 );
|
|
* hssfCell.setCellValue( richString );
|
|
* </pre></blockquote><p>
|
|
*
|
|
* Excel always uses the latter approach: for a reach text containing N runs Excel saves the font of the first run in the cell's
|
|
* style and subsequent N-1 runs override this font.
|
|
*
|
|
* <p> For more information regarding this behavior please consult Bugzilla 47543:
|
|
*
|
|
* <a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=47543">
|
|
* https://issues.apache.org/bugzilla/show_bug.cgi?id=47543</a>
|
|
*/
|
|
public final class HSSFRichTextString implements Comparable<HSSFRichTextString>, RichTextString {
|
|
/** Place holder for indicating that NO_FONT has been applied here */
|
|
public static final short NO_FONT = 0;
|
|
|
|
private UnicodeString _string;
|
|
private InternalWorkbook _book;
|
|
private LabelSSTRecord _record;
|
|
|
|
public HSSFRichTextString()
|
|
{
|
|
this("");
|
|
}
|
|
|
|
public HSSFRichTextString(String string) {
|
|
if (string == null) {
|
|
_string = new UnicodeString("");
|
|
} else {
|
|
_string = new UnicodeString(string);
|
|
}
|
|
}
|
|
|
|
HSSFRichTextString(InternalWorkbook book, LabelSSTRecord record) {
|
|
setWorkbookReferences(book, record);
|
|
|
|
_string = book.getSSTString(record.getSSTIndex());
|
|
}
|
|
|
|
/** This must be called to setup the internal work book references whenever
|
|
* a RichTextString is added to a cell
|
|
*/
|
|
void setWorkbookReferences(InternalWorkbook book, LabelSSTRecord record) {
|
|
_book = book;
|
|
_record = record;
|
|
}
|
|
|
|
/** Called whenever the unicode string is modified. When it is modified
|
|
* we need to create a new SST index, so that other LabelSSTRecords will not
|
|
* be affected by changes that we make to this string.
|
|
*/
|
|
private UnicodeString cloneStringIfRequired() {
|
|
if (_book == null)
|
|
return _string;
|
|
UnicodeString s = (UnicodeString)_string.clone();
|
|
return s;
|
|
}
|
|
|
|
private void addToSSTIfRequired() {
|
|
if (_book != null) {
|
|
int index = _book.addSSTString(_string);
|
|
_record.setSSTIndex(index);
|
|
//The act of adding the string to the SST record may have meant that
|
|
//an existing string was returned for the index, so update our local version
|
|
_string = _book.getSSTString(index);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
if (startIndex > endIndex)
|
|
throw new IllegalArgumentException("Start index must be less than end index.");
|
|
if (startIndex < 0 || endIndex > length())
|
|
throw new IllegalArgumentException("Start and end index not in range.");
|
|
if (startIndex == endIndex)
|
|
return;
|
|
|
|
//Need to check what the font is currently, so we can reapply it after
|
|
//the range is completed
|
|
short currentFont = NO_FONT;
|
|
if (endIndex != length()) {
|
|
currentFont = this.getFontAtIndex(endIndex);
|
|
}
|
|
|
|
//Need to clear the current formatting between the startIndex and endIndex
|
|
_string = cloneStringIfRequired();
|
|
Iterator<FormatRun> formatting = _string.formatIterator();
|
|
if (formatting != null) {
|
|
while (formatting.hasNext()) {
|
|
UnicodeString.FormatRun r = formatting.next();
|
|
if ((r.getCharacterPos() >= startIndex) && (r.getCharacterPos() < endIndex))
|
|
formatting.remove();
|
|
}
|
|
}
|
|
|
|
|
|
_string.addFormatRun(new UnicodeString.FormatRun((short)startIndex, fontIndex));
|
|
if (endIndex != length())
|
|
_string.addFormatRun(new UnicodeString.FormatRun((short)endIndex, currentFont));
|
|
|
|
addToSSTIfRequired();
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
applyFont(startIndex, endIndex, ((HSSFFont) font).getIndex());
|
|
}
|
|
|
|
/**
|
|
* Sets the font of the entire string.
|
|
* @param font The font to use.
|
|
*/
|
|
public void applyFont(Font font)
|
|
{
|
|
applyFont(0, _string.getCharCount(), font);
|
|
}
|
|
|
|
/**
|
|
* Removes any formatting that may have been applied to the string.
|
|
*/
|
|
public void clearFormatting() {
|
|
_string = cloneStringIfRequired();
|
|
_string.clearFormatting();
|
|
addToSSTIfRequired();
|
|
}
|
|
|
|
/**
|
|
* Returns the plain string representation.
|
|
*/
|
|
public String getString()
|
|
{
|
|
return _string.getString();
|
|
}
|
|
|
|
/**
|
|
* Used internally by the HSSFCell to get the internal
|
|
* string value.
|
|
* Will ensure the string is not shared
|
|
*/
|
|
UnicodeString getUnicodeString() {
|
|
return cloneStringIfRequired();
|
|
}
|
|
|
|
/**
|
|
* Returns the raw, probably shared Unicode String.
|
|
* Used when tweaking the styles, eg updating font
|
|
* positions.
|
|
* Changes to this string may well effect
|
|
* other RichTextStrings too!
|
|
*/
|
|
UnicodeString getRawUnicodeString() {
|
|
return _string;
|
|
}
|
|
|
|
/** Used internally by the HSSFCell to set the internal string value*/
|
|
void setUnicodeString(UnicodeString str) {
|
|
this._string = str;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return the number of characters in the text.
|
|
*/
|
|
public int length() {
|
|
return _string.getCharCount();
|
|
}
|
|
|
|
/**
|
|
* Returns the font in use at a particular index.
|
|
*
|
|
* @param index The index.
|
|
* @return 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 short getFontAtIndex( int index )
|
|
{
|
|
int size = _string.getFormatRunCount();
|
|
UnicodeString.FormatRun currentRun = null;
|
|
for (int i=0;i<size;i++) {
|
|
UnicodeString.FormatRun r = _string.getFormatRun(i);
|
|
if (r.getCharacterPos() > index) {
|
|
break;
|
|
}
|
|
currentRun = r;
|
|
}
|
|
if (currentRun == null) {
|
|
return NO_FONT;
|
|
}
|
|
return currentRun.getFontIndex();
|
|
}
|
|
|
|
/**
|
|
* @return The number of formatting runs used. There will always be at
|
|
* least one of font NO_FONT.
|
|
*
|
|
* @see #NO_FONT
|
|
*/
|
|
public int numFormattingRuns()
|
|
{
|
|
return _string.getFormatRunCount();
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
UnicodeString.FormatRun r = _string.getFormatRun(index);
|
|
return r.getCharacterPos();
|
|
}
|
|
|
|
/**
|
|
* Gets the font used in a particular formatting run.
|
|
*
|
|
* @param index the index of the formatting run
|
|
* @return the font number used.
|
|
*/
|
|
public short getFontOfFormattingRun(int index)
|
|
{
|
|
UnicodeString.FormatRun r = _string.getFormatRun(index);
|
|
return r.getFontIndex();
|
|
}
|
|
|
|
/**
|
|
* Compares one rich text string to another.
|
|
*/
|
|
public int compareTo(HSSFRichTextString r) {
|
|
return _string.compareTo(r._string);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (o instanceof HSSFRichTextString) {
|
|
return _string.equals(((HSSFRichTextString)o)._string);
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
assert false : "hashCode not designed";
|
|
return 42; // any arbitrary constant will do
|
|
}
|
|
|
|
|
|
/**
|
|
* @return the plain text representation of this string.
|
|
*/
|
|
public String toString()
|
|
{
|
|
return _string.toString();
|
|
}
|
|
|
|
/**
|
|
* Applies the specified font to the entire string.
|
|
*
|
|
* @param fontIndex the font to apply.
|
|
*/
|
|
public void applyFont( short fontIndex )
|
|
{
|
|
applyFont(0, _string.getCharCount(), fontIndex);
|
|
}
|
|
}
|