| /* ==================================================================== |
| 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.hssf.usermodel; |
| |
| import java.util.Iterator; |
| |
| import org.apache.poi.hssf.model.InternalWorkbook; |
| import org.apache.poi.hssf.record.LabelSSTRecord; |
| import org.apache.poi.hssf.record.common.UnicodeString; |
| import org.apache.poi.hssf.record.common.UnicodeString.FormatRun; |
| import org.apache.poi.ss.usermodel.Font; |
| import org.apache.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); |
| } |
| } |