Comment support from bug 41198, patch from Yegor

git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@491629 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2007-01-01 21:02:22 +00:00
parent c5b9d25328
commit e423cc45ed
20 changed files with 1225 additions and 9 deletions

View File

@ -47,6 +47,7 @@
<li><link href="#Outlining">Outlining</link></li>
<li><link href="#Images">Images</link></li>
<li><link href="#NamedRanges">Named Ranges and Named Cells</link></li>
<li><link href="#CellComments">How to set cell comments</link></li>
</ul>
</section>
<section><title>Features</title>
@ -1034,6 +1035,85 @@
</source>
</section>
<anchor id="CellComments"/>
<section><title>Cell Comments</title>
<p>
In Excel a comment is a kind of a text shape,
so inserting a comment is very similar to placing a text box in a worksheet:
</p>
<source>
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Cell comments in POI HSSF");
// Create the drawing patriarch. This is the top level container for all shapes including cell comments.
HSSFPatriarch patr = sheet.createDrawingPatriarch();
//create a cell in row 3
HSSFCell cell1 = sheet.createRow(3).createCell((short)1);
cell1.setCellValue(new HSSFRichTextString("Hello, World"));
//anchor defines size and position of the comment in worksheet
HSSFComment comment1 = patr.createComment(new HSSFClientAnchor(0, 0, 0, 0, (short)4, 2, (short) 6, 5));
// set text in the comment
comment1.setString(new HSSFRichTextString("We can set comments in POI"));
//set comment author.
//you can see it in the status bar when moving mouse over the commented cell
comment1.setAuthor("Apache Software Foundation");
// The first way to assign comment to a cell is via HSSFCell.setCellComment method
cell1.setCellComment(comment1);
//create another cell in row 6
HSSFCell cell2 = sheet.createRow(6).createCell((short)1);
cell2.setCellValue(36.6);
HSSFComment comment2 = patr.createComment(new HSSFClientAnchor(0, 0, 0, 0, (short)4, 8, (short) 6, 11));
//modify background color of the comment
comment2.setFillColor(204, 236, 255);
HSSFRichTextString string = new HSSFRichTextString("Normal body temperature");
//apply custom font to the text in the comment
HSSFFont font = wb.createFont();
font.setFontName("Arial");
font.setFontHeightInPoints((short)10);
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
font.setColor(HSSFColor.RED.index);
string.applyFont(font);
comment2.setString(string);
//by default comments are hidden. This one is always visible.
comment2.setVisible(true);
comment2.setAuthor("Bill Gates");
/**
* The second way to assign comment to a cell is to implicitly specify its row and column.
* Note, it is possible to set row and column of a non-existing cell.
* It works, the commnet is visible.
*/
comment2.setRow(6);
comment2.setColumn((short)1);
FileOutputStream out = new FileOutputStream("poi_comment.xls");
wb.write(out);
out.close();
</source>
<p>
Reading cell comments
</p>
<source>
HSSFCell cell = sheet.get(3).getColumn((short)1);
HSSFComment comment = cell.getCellComment();
if (comment != null) {
HSSFRichTextString str = comment.getString();
String author = comment.getAuthor();
}
</source>
</section>
</body>
</document>

View File

@ -0,0 +1,99 @@
/* ====================================================================
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.examples;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import java.io.*;
/**
* Demonstrates how to work with excel cell comments.
*
* <p>
* Excel comment is a kind of a text shape,
* so inserting a comment is very similar to placing a text box in a worksheet
* </p>
*
* @author Yegor Kozlov
*/
public class CellComments {
public static void main(String[] args) throws IOException {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Cell comments in POI HSSF");
// Create the drawing patriarch. This is the top level container for all shapes including cell comments.
HSSFPatriarch patr = sheet.createDrawingPatriarch();
//create a cell in row 3
HSSFCell cell1 = sheet.createRow(3).createCell((short)1);
cell1.setCellValue(new HSSFRichTextString("Hello, World"));
//anchor defines size and position of the comment in worksheet
HSSFComment comment1 = patr.createComment(new HSSFClientAnchor(0, 0, 0, 0, (short)4, 2, (short) 6, 5));
// set text in the comment
comment1.setString(new HSSFRichTextString("We can set comments in POI"));
//set comment author.
//you can see it in the status bar when moving mouse over the commented cell
comment1.setAuthor("Apache Software Foundation");
// The first way to assign comment to a cell is via HSSFCell.setCellComment method
cell1.setCellComment(comment1);
//create another cell in row 6
HSSFCell cell2 = sheet.createRow(6).createCell((short)1);
cell2.setCellValue(36.6);
HSSFComment comment2 = patr.createComment(new HSSFClientAnchor(0, 0, 0, 0, (short)4, 8, (short) 6, 11));
//modify background color of the comment
comment2.setFillColor(204, 236, 255);
HSSFRichTextString string = new HSSFRichTextString("Normal body temperature");
//apply custom font to the text in the comment
HSSFFont font = wb.createFont();
font.setFontName("Arial");
font.setFontHeightInPoints((short)10);
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
font.setColor(HSSFColor.RED.index);
string.applyFont(font);
comment2.setString(string);
comment2.setVisible(true); //by default comments are hidden. This one is always visible.
comment2.setAuthor("Bill Gates");
/**
* The second way to assign comment to a cell is to implicitly specify its row and column.
* Note, it is possible to set row and column of a non-existing cell.
* It works, the commnet is visible.
*/
comment2.setRow(6);
comment2.setColumn((short)1);
FileOutputStream out = new FileOutputStream("poi_comment.xls");
wb.write(out);
out.close();
}
}

View File

@ -509,6 +509,9 @@ public class BiffViewer {
case FilePassRecord.sid:
retval = new FilePassRecord(in);
break;
case NoteRecord.sid:
retval = new NoteRecord( in );
break;
default:
retval = new UnknownRecord( in );
}

View File

@ -107,6 +107,7 @@ import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.hssf.record.WriteAccessRecord;
import org.apache.poi.hssf.record.WriteProtectRecord;
import org.apache.poi.hssf.record.FilePassRecord;
import org.apache.poi.hssf.record.NoteRecord;
/**
@ -158,7 +159,8 @@ public class EventRecordFactory
LeftMarginRecord.class, RightMarginRecord.class,
TopMarginRecord.class, BottomMarginRecord.class,
PaletteRecord.class, StringRecord.class, SharedFormulaRecord.class,
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
NoteRecord.class
};
}

View File

@ -36,7 +36,11 @@ public abstract class AbstractShape
public static AbstractShape createShape( HSSFShape hssfShape, int shapeId )
{
AbstractShape shape;
if (hssfShape instanceof HSSFTextbox)
if (hssfShape instanceof HSSFComment)
{
shape = new CommentShape( (HSSFComment)hssfShape, shapeId );
}
else if (hssfShape instanceof HSSFTextbox)
{
shape = new TextboxShape( (HSSFTextbox)hssfShape, shapeId );
}

View File

@ -0,0 +1,133 @@
/* ====================================================================
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.model;
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.usermodel.HSSFComment;
import org.apache.poi.hssf.usermodel.HSSFShape;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.ddf.*;
import java.util.List;
import java.util.Iterator;
/**
* Represents a cell comment.
* This class converts highlevel model data from <code>HSSFComment</code>
* to low-level records.
*
* @author Yegor Kozlov
*/
public class CommentShape extends TextboxShape {
private NoteRecord note;
/**
* Creates the low-level records for a comment.
*
* @param hssfShape The highlevel shape.
* @param shapeId The shape id to use for this shape.
*/
public CommentShape( HSSFComment hssfShape, int shapeId )
{
super(hssfShape, shapeId);
note = createNoteRecord(hssfShape, shapeId);
ObjRecord obj = getObjRecord();
List records = obj.getSubRecords();
int cmoIdx = 0;
for (int i = 0; i < records.size(); i++) {
Object r = records.get(i);
if (r instanceof CommonObjectDataSubRecord){
//modify autofill attribute inherited from <code>TextObjectRecord</code>
CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord)r;
cmo.setAutofill(false);
cmoIdx = i;
}
}
//add NoteStructure sub record
//we don't know it's format, for now the record data is empty
NoteStructureSubRecord u = new NoteStructureSubRecord();
obj.addSubRecord(cmoIdx+1, u);
}
/**
* Creates the low level <code>NoteRecord</code>
* which holds the comment attributes.
*/
private NoteRecord createNoteRecord( HSSFComment shape, int shapeId )
{
NoteRecord note = new NoteRecord();
note.setColumn(shape.getColumn());
note.setRow((short)shape.getRow());
note.setFlags(shape.isVisible() ? NoteRecord.NOTE_VISIBLE : NoteRecord.NOTE_HIDDEN);
note.setShapeId((short)shapeId);
note.setAuthor(shape.getAuthor() == null ? "" : shape.getAuthor());
return note;
}
/**
* Sets standard escher options for a comment.
* This method is responsible for setting default background,
* shading and other comment properties.
*
* @param shape The highlevel shape.
* @param opt The escher records holding the proerties
* @return number of escher options added
*/
protected int addStandardOptions( HSSFShape shape, EscherOptRecord opt )
{
super.addStandardOptions(shape, opt);
//remove unnecessary properties inherited from TextboxShape
java.util.List props = opt.getEscherProperties();
for ( Iterator iterator = props.iterator(); iterator.hasNext(); ) {
EscherProperty prop = (EscherProperty) iterator.next();
switch (prop.getId()){
case EscherProperties.TEXT__TEXTLEFT:
case EscherProperties.TEXT__TEXTRIGHT:
case EscherProperties.TEXT__TEXTTOP:
case EscherProperties.TEXT__TEXTBOTTOM:
case EscherProperties.GROUPSHAPE__PRINT:
case EscherProperties.FILL__FILLBACKCOLOR:
case EscherProperties.LINESTYLE__COLOR:
iterator.remove();
break;
}
}
HSSFComment comment = (HSSFComment)shape;
opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.GROUPSHAPE__PRINT, comment.isVisible() ? 0x000A0000 : 0x000A0002) );
opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.SHADOWSTYLE__SHADOWOBSURED, 0x00030003 ) );
opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.SHADOWSTYLE__COLOR, 0x00000000 ) );
opt.sortProperties();
return opt.getEscherProperties().size(); // # options added
}
/**
* Return the <code>NoteRecord</code> holding the comment attributes
*
* @return <code>NoteRecord</code> holding the comment attributes
*/
public NoteRecord getNoteRecord()
{
return note;
}
}

View File

@ -23,6 +23,7 @@ import org.apache.poi.hssf.model.AbstractShape;
import org.apache.poi.hssf.model.TextboxShape;
import org.apache.poi.hssf.model.DrawingManager2;
import org.apache.poi.hssf.model.ConvertAnchor;
import org.apache.poi.hssf.model.CommentShape;
import java.util.*;
@ -260,6 +261,11 @@ public class EscherAggregate extends AbstractEscherHolderRecord
private DrawingManager2 drawingManager;
private short drawingGroupId;
/**
* list of "tail" records that need to be serialized after all drawing group records
*/
private List tailRec = new ArrayList();
public EscherAggregate( DrawingManager2 drawingManager )
{
this.drawingManager = drawingManager;
@ -450,6 +456,13 @@ public class EscherAggregate extends AbstractEscherHolderRecord
}
// write records that need to be serialized after all drawing group records
for ( int i = 0; i < tailRec.size(); i++ )
{
Record rec = (Record)tailRec.get(i);
pos += rec.serialize( pos, data );
}
int bytesWritten = pos - offset;
if ( bytesWritten != getRecordSize() )
throw new RecordFormatException( bytesWritten + " bytes written but getRecordSize() reports " + getRecordSize() );
@ -484,7 +497,13 @@ public class EscherAggregate extends AbstractEscherHolderRecord
Record r = (Record) iterator.next();
objRecordSize += r.getRecordSize();
}
return drawingRecordSize + objRecordSize;
int tailRecordSize = 0;
for ( Iterator iterator = tailRec.iterator(); iterator.hasNext(); )
{
Record r = (Record) iterator.next();
tailRecordSize += r.getRecordSize();
}
return drawingRecordSize + objRecordSize + tailRecordSize;
}
/**
@ -529,6 +548,7 @@ public class EscherAggregate extends AbstractEscherHolderRecord
if ( patriarch != null )
{
shapeToObj.clear();
tailRec.clear();
clearEscherRecords();
if ( patriarch.getChildren().size() != 0 )
{
@ -568,6 +588,12 @@ public class EscherAggregate extends AbstractEscherHolderRecord
EscherRecord escherTextbox = ( (TextboxShape) shapeModel ).getEscherTextbox();
shapeToObj.put( escherTextbox, ( (TextboxShape) shapeModel ).getTextObjectRecord() );
// escherParent.addChildRecord(escherTextbox);
if ( shapeModel instanceof CommentShape ){
CommentShape comment = (CommentShape)shapeModel;
tailRec.add(comment.getNoteRecord());
}
}
escherParent.addChildRecord( shapeModel.getSpContainer() );
}

View File

@ -0,0 +1,246 @@
/* ====================================================================
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.record;
import org.apache.poi.util.LittleEndian;
/**
* NOTE: Comment Associated with a Cell (1Ch)
*
* @author Yegor Kozlov
*/
public class NoteRecord extends Record {
public final static short sid = 0x1C;
/**
* Flag indicating that the comment is hidden (default)
*/
public final static short NOTE_HIDDEN = 0x0;
/**
* Flag indicating that the comment is visible
*/
public final static short NOTE_VISIBLE = 0x2;
private short field_1_row;
private short field_2_col;
private short field_3_flags;
private short field_4_shapeid;
private String field_5_author;
/**
* Construct a new <code>NoteRecord</code> and
* fill its data with the default values
*/
public NoteRecord()
{
field_5_author = "";
field_3_flags = 0;
}
/**
* Constructs a <code>NoteRecord</code> and fills its fields
* from the supplied <code>RecordInputStream</code>.
*
* @param in the stream to read from
*/
public NoteRecord(RecordInputStream in)
{
super(in);
}
/**
* @return id of this record.
*/
public short getSid()
{
return sid;
}
/**
* Checks the sid matches the expected side for this record
*
* @param id the expected sid.
*/
protected void validateSid(short id)
{
if (id != sid)
{
throw new RecordFormatException("Not a NoteRecord record");
}
}
/**
* Read the record data from the supplied <code>RecordInputStream</code>
*/
protected void fillFields(RecordInputStream in)
{
field_1_row = in.readShort();
field_2_col = in.readShort();
field_3_flags = in.readShort();
field_4_shapeid = in.readShort();
int length = in.readShort();
byte[] bytes = in.readRemainder();
field_5_author = new String(bytes, 1, length);
}
/**
* Serialize the record data into the supplied array of bytes
*
* @param offset offset in the <code>data</code>
* @param data the data to serialize into
*
* @return size of the record
*/
public int serialize(int offset, byte [] data)
{
LittleEndian.putShort(data, 0 + offset, sid);
LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
LittleEndian.putShort(data, 4 + offset , field_1_row);
LittleEndian.putShort(data, 6 + offset , field_2_col);
LittleEndian.putShort(data, 8 + offset , field_3_flags);
LittleEndian.putShort(data, 10 + offset , field_4_shapeid);
LittleEndian.putShort(data, 12 + offset , (short)field_5_author.length());
byte[] str = field_5_author.getBytes();
System.arraycopy(str, 0, data, 15 + offset, str.length);
return getRecordSize();
}
/**
* Size of record
*/
public int getRecordSize()
{
int retval = 4 + 2 + 2 + 2 + 2 + 2 + 1 + field_5_author.length() + 1;
return retval;
}
/**
* Convert this record to string.
* Used by BiffViewer and other utulities.
*/
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append("[NOTE]\n");
buffer.append(" .recordid = 0x" + Integer.toHexString( getSid() ) + ", size = " + getRecordSize() + "\n");
buffer.append(" .row = " + field_1_row + "\n");
buffer.append(" .col = " + field_2_col + "\n");
buffer.append(" .flags = " + field_3_flags + "\n");
buffer.append(" .shapeid = " + field_4_shapeid + "\n");
buffer.append(" .author = " + field_5_author + "\n");
buffer.append("[/NOTE]\n");
return buffer.toString();
}
/**
* Return the row that contains the comment
*
* @return the row that contains the comment
*/
public short getRow(){
return field_1_row;
}
/**
* Specify the row that contains the comment
*
* @param row the row that contains the comment
*/
public void setRow(short row){
field_1_row = row;
}
/**
* Return the column that contains the comment
*
* @return the column that contains the comment
*/
public short getColumn(){
return field_2_col;
}
/**
* Specify the column that contains the comment
*
* @param col the column that contains the comment
*/
public void setColumn(short col){
field_2_col = col;
}
/**
* Options flags.
*
* @return the options flag
* @see NoteRecord.NOTE_VISIBLE
* @see NoteRecord.NOTE_HIDDEN
*/
public short getFlags(){
return field_3_flags;
}
/**
* Options flag
*
* @param flags the options flag
* @see #NOTE_VISIBLE
* @see #NOTE_HIDDEN
*/
public void setFlags(short flags){
field_3_flags = flags;
}
/**
* Object id for OBJ record that contains the comment
*/
public short getShapeId(){
return field_4_shapeid;
}
/**
* Object id for OBJ record that contains the comment
*/
public void setShapeId(short id){
field_4_shapeid = id;
}
/**
* Name of the original comment author
*
* @return the name of the original author of the comment
*/
public String getAuthor(){
return field_5_author;
}
/**
* Name of the original comment author
*
* @param author the name of the original author of the comment
*/
public void setAuthor(String author){
field_5_author = author;
}
}

View File

@ -0,0 +1,130 @@
/* ====================================================================
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.record;
import org.apache.poi.util.*;
/**
* Represents a NoteStructure (0xD) sub record.
*
* <p>
* The docs say nothing about it. The length of this record is always 26 bytes.
* </p>
*
* @author Yegor Kozlov
*/
public class NoteStructureSubRecord
extends SubRecord
{
public final static short sid = 0x0D;
private byte[] reserved;
/**
* Construct a new <code>NoteStructureSubRecord</code> and
* fill its data with the default values
*/
public NoteStructureSubRecord()
{
//all we know is that the the length of <code>NoteStructureSubRecord</code> is always 22 bytes
reserved = new byte[22];
}
/**
* Constructs a NoteStructureSubRecord and sets its fields appropriately.
*
*/
public NoteStructureSubRecord(RecordInputStream in)
{
super(in);
}
/**
* Checks the sid matches the expected side for this record
*
* @param id the expected sid.
*/
protected void validateSid(short id)
{
if (id != sid)
{
throw new RecordFormatException("Not a Note Structure record");
}
}
/**
* Read the record data from the supplied <code>RecordInputStream</code>
*/
protected void fillFields(RecordInputStream in)
{
//just grab the raw data
reserved = in.readRemainder();
}
/**
* Convert this record to string.
* Used by BiffViewer and other utulities.
*/
public String toString()
{
StringBuffer buffer = new StringBuffer();
String nl = System.getProperty("line.separator");
buffer.append("[ftNts ]" + nl);
buffer.append(" size = ").append(getRecordSize()).append(nl);
buffer.append(" reserved = ").append(HexDump.toHex(reserved)).append(nl);
buffer.append("[/ftNts ]" + nl);
return buffer.toString();
}
/**
* Serialize the record data into the supplied array of bytes
*
* @param offset offset in the <code>data</code>
* @param data the data to serialize into
*
* @return size of the record
*/
public int serialize(int offset, byte[] data)
{
LittleEndian.putShort(data, 0 + offset, sid);
LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
System.arraycopy(reserved, 0, data, offset + 4, getRecordSize() - 4);
return getRecordSize();
}
/**
* Size of record
*/
public int getRecordSize()
{
return 4 + reserved.length;
}
/**
* @return id of this record.
*/
public short getSid()
{
return sid;
}
}

View File

@ -73,7 +73,8 @@ public class RecordFactory
ObjRecord.class, TextObjectRecord.class,
PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class,
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
NoteRecord.class
};
}
private static Map recordsMap = recordsToMap(records);

View File

@ -64,6 +64,9 @@ abstract public class SubRecord
case EndSubRecord.sid:
r = new EndSubRecord( in );
break;
case NoteStructureSubRecord.sid:
r = new NoteStructureSubRecord( in );
break;
default:
r = new UnknownRecord( in );
}

View File

@ -33,8 +33,7 @@ import org.apache.poi.hssf.record.formula.Ptg;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.*;
/**
* High level representation of a cell in a row of a spreadsheet.
@ -51,6 +50,7 @@ import java.util.Date;
* @author Andrew C. Oliver (acoliver at apache dot org)
* @author Dan Sherman (dsherman at isisph.com)
* @author Brian Sanders (kestrel at burdell dot org) Active Cell support
* @author Yegor Kozlov cell comments support
* @version 1.0-pre
*/
@ -113,6 +113,7 @@ public class HSSFCell
private Workbook book;
private Sheet sheet;
private CellValueRecordInterface record;
private HSSFComment comment;
/**
* Creates new Cell - Should only be called by HSSFRow. This creates a cell
@ -959,4 +960,60 @@ public class HSSFCell
return "Unknown Cell Type: " + getCellType();
}
}
/**
* Assign a comment to this cell
*
* @param comment comment associated with this cell
*/
public void setCellComment(HSSFComment comment){
comment.setRow((short)record.getRow());
comment.setColumn(record.getColumn());
this.comment = comment;
}
/**
* Returns the comment associated with this cell
*
* @return comment associated with this cell
*/
public HSSFComment getCellComment(){
if (comment == null) {
HashMap txshapes = new HashMap(); //map shapeId and TextObjectRecord
for (Iterator it = sheet.getRecords().iterator(); it.hasNext(); ) {
Record rec = ( Record ) it.next();
if (rec instanceof NoteRecord){
NoteRecord note = (NoteRecord)rec;
if (note.getRow() == record.getRow() && note.getColumn() == record.getColumn()){
TextObjectRecord txo = (TextObjectRecord)txshapes.get(new Integer(note.getShapeId()));
comment = new HSSFComment(null, null);
comment.setRow(note.getRow());
comment.setColumn(note.getColumn());
comment.setAuthor(note.getAuthor());
comment.setVisible(note.getFlags() == NoteRecord.NOTE_VISIBLE);
comment.setString(txo.getStr());
break;
}
} else if (rec instanceof ObjRecord){
ObjRecord obj = (ObjRecord)rec;
SubRecord sub = (SubRecord)obj.getSubRecords().get(0);
if (sub instanceof CommonObjectDataSubRecord){
CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord)sub;
if (cmo.getObjectType() == CommonObjectDataSubRecord.OBJECT_TYPE_COMMENT){
//find the nearest TextObjectRecord which holds comment's text and map it to its shapeId
while(it.hasNext()) {
rec = ( Record ) it.next();
if (rec instanceof TextObjectRecord) {
txshapes.put(new Integer(cmo.getObjectId()), rec);
break;
}
}
}
}
}
}
}
return comment;
}
}

View File

@ -0,0 +1,143 @@
/* ====================================================================
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 org.apache.poi.hssf.record.EscherAggregate;
import org.apache.poi.hssf.record.NoteRecord;
import org.apache.poi.hssf.record.TextObjectRecord;
import org.apache.poi.ddf.*;
import java.util.Map;
import java.util.List;
import java.util.Iterator;
/**
* Represents a cell comment - a sticky note associated with a cell.
*
* @author Yegor Kolzlov
*/
public class HSSFComment extends HSSFTextbox {
private boolean visible;
private short col, row;
private String author;
/**
* Construct a new comment with the given parent and anchor.
*
* @param parent
* @param anchor defines position of this anchor in the sheet
*/
public HSSFComment( HSSFShape parent, HSSFAnchor anchor )
{
super( parent, anchor );
setShapeType(OBJECT_TYPE_COMMENT);
//default color for comments
fillColor = 0x08000050;
//by default comments are hidden
visible = false;
author = "";
}
/**
* Returns whether this comment is visible.
*
* @param visible <code>true</code> if the comment is visible, <code>false</code> otherwise
*/
public void setVisible(boolean visible){
this.visible = visible;
}
/**
* Sets whether this comment is visible.
*
* @return <code>true</code> if the comment is visible, <code>false</code> otherwise
*/
public boolean isVisible(){
return this.visible;
}
/**
* Return the row of the cell that contains the comment
*
* @return the 0-based row of the cell that contains the comment
*/
public int getRow(){
return row;
}
/**
* Set the row of the cell that contains the comment
*
* @param row the 0-based row of the cell that contains the comment
*/
public void setRow(int row){
this.row = (short)row;
}
/**
* Return the column of the cell that contains the comment
*
* @return the 0-based column of the cell that contains the comment
*/
public short getColumn(){
return col;
}
/**
* Set the column of the cell that contains the comment
*
* @param col the 0-based column of the cell that contains the comment
*/
public void setColumn(short col){
this.col = col;
}
/**
* Name of the original comment author
*
* @return the name of the original author of the comment
*/
public String getAuthor(){
return author;
}
/**
* Name of the original comment author
*
* @param author the name of the original author of the comment
*/
public void setAuthor(String author){
this.author = author;
}
/**
* Sets the rich text string used by this comment.
*
* @param string Sets the rich text string used by this object.
*/
public void setString( HSSFRichTextString string )
{
//if font is not set we must set the default one implicitly
if (string.numFormattingRuns() == 0) string.applyFont((short)0);
super.setString(string);
}
}

View File

@ -125,6 +125,21 @@ public class HSSFPatriarch
return shape;
}
/**
* Constructs a cell comment.
*
* @param anchor the client anchor describes how this comment is attached
* to the sheet.
* @return the newly created comment.
*/
public HSSFComment createComment(HSSFAnchor anchor)
{
HSSFComment shape = new HSSFComment(null, anchor);
shape.anchor = anchor;
shapes.add(shape);
return shape;
}
/**
* Returns a list of all shapes contained by the patriarch.
*/

View File

@ -47,7 +47,7 @@ public class HSSFSimpleShape
// public final static short OBJECT_TYPE_LIST_BOX = 18;
// public final static short OBJECT_TYPE_GROUP_BOX = 19;
// public final static short OBJECT_TYPE_COMBO_BOX = 20;
// public final static short OBJECT_TYPE_COMMENT = 25;
public final static short OBJECT_TYPE_COMMENT = 25;
// public final static short OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING = 30;
int shapeType = OBJECT_TYPE_LINE;
@ -65,6 +65,7 @@ public class HSSFSimpleShape
* @see #OBJECT_TYPE_OVAL
* @see #OBJECT_TYPE_RECTANGLE
* @see #OBJECT_TYPE_PICTURE
* @see #OBJECT_TYPE_COMMENT
*/
public int getShapeType() { return shapeType; }
@ -77,6 +78,7 @@ public class HSSFSimpleShape
* @see #OBJECT_TYPE_OVAL
* @see #OBJECT_TYPE_RECTANGLE
* @see #OBJECT_TYPE_PICTURE
* @see #OBJECT_TYPE_COMMENT
*/
public void setShapeType( int shapeType ){ this.shapeType = shapeType; }

View File

@ -110,6 +110,7 @@ import org.apache.poi.hssf.util.TestCellReference;
import org.apache.poi.hssf.util.TestRKUtil;
import org.apache.poi.hssf.util.TestRangeAddress;
import org.apache.poi.hssf.util.TestSheetReferences;
import org.apache.poi.hssf.usermodel.TestHSSFComment;
/**
* Test Suite for running just HSSF tests. Mostly
@ -227,7 +228,8 @@ public class HSSFTests
suite.addTest(new TestSuite(TestModelFactory.class));
suite.addTest(new TestSuite(TestDrawingManager.class));
suite.addTest(new TestSuite(TestSheet.class));
suite.addTest(new TestSuite(TestHSSFComment.class));
//$JUnit-END$
return suite;
}

View File

@ -0,0 +1,82 @@
/* ====================================================================
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.record;
import junit.framework.TestCase;
import java.util.Arrays;
/**
* Tests the serialization and deserialization of the NoteRecord
* class works correctly. Test data taken directly from a real
* Excel file.
*
* @author Yegor Kozlov
*/
public class TestNoteRecord
extends TestCase
{
private byte[] data = new byte[] {
0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x04, 0x1A, 0x00,
0x00, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x20, 0x53, 0x6F,
0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x46, 0x6F, 0x75,
0x6E, 0x64, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x00
};
public TestNoteRecord(String name)
{
super(name);
}
public void testRead()
throws Exception
{
NoteRecord record = new NoteRecord(new TestcaseRecordInputStream(NoteRecord.sid, (short)data.length, data));
assertEquals(NoteRecord.sid, record.getSid());
record.validateSid(NoteRecord.sid);
assertEquals(6, record.getRow());
assertEquals(1, record.getColumn());
assertEquals(NoteRecord.NOTE_VISIBLE, record.getFlags());
assertEquals(1026, record.getShapeId());
assertEquals("Apache Software Foundation", record.getAuthor());
}
public void testWrite()
{
NoteRecord record = new NoteRecord();
assertEquals(NoteRecord.sid, record.getSid());
record.validateSid(NoteRecord.sid);
record.setRow((short)6);
record.setColumn((short)1);
record.setFlags(NoteRecord.NOTE_VISIBLE);
record.setShapeId((short)1026);
record.setAuthor("Apache Software Foundation");
byte [] ser = record.serialize();
assertEquals(ser.length - 4, data.length);
byte[] recdata = new byte[ser.length - 4];
System.arraycopy(ser, 4, recdata, 0, recdata.length);
assertTrue(Arrays.equals(data, recdata));
}
}

View File

@ -0,0 +1,68 @@
/* ====================================================================
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.record;
import junit.framework.TestCase;
import java.util.Arrays;
/**
* Tests the serialization and deserialization of the NoteRecord
* class works correctly. Test data taken directly from a real
* Excel file.
*
* @author Yegor Kozlov
*/
public class TestNoteStructureSubRecord
extends TestCase
{
private byte[] data = new byte[] {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0x80, 0x00, 0x00, 0x00,
0x00, 0x00, (byte)0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0x81, 0x01,
(byte)0xCC, (byte)0xEC
};
public TestNoteStructureSubRecord(String name)
{
super(name);
}
public void testRead()
throws Exception
{
NoteStructureSubRecord record = new NoteStructureSubRecord(new TestcaseRecordInputStream(NoteStructureSubRecord.sid, (short)data.length, data));
assertEquals(NoteStructureSubRecord.sid, record.getSid());
record.validateSid(NoteStructureSubRecord.sid);
assertEquals(data.length + 4, record.getRecordSize());
}
public void testWrite()
{
NoteStructureSubRecord record = new NoteStructureSubRecord();
assertEquals(NoteStructureSubRecord.sid, record.getSid());
record.validateSid(NoteStructureSubRecord.sid);
assertEquals(data.length + 4, record.getRecordSize());
byte [] ser = record.serialize();
assertEquals(ser.length - 4, data.length);
}
}

View File

@ -0,0 +1,120 @@
/* ====================================================================
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 junit.framework.TestCase;
import java.io.*;
/**
* Tests TestHSSFCellComment.
*
* @author Yegor Kozlov
*/
public class TestHSSFComment extends TestCase {
/**
* Test that we can create cells and add comments to it.
*/
public static void testWriteComments() throws Exception {
String cellText = "Hello, World";
String commentText = "We can set comments in POI";
String commentAuthor = "Apache Software Foundation";
int cellRow = 3;
short cellColumn = 1;
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
HSSFCell cell = sheet.createRow(cellRow).createCell(cellColumn);
cell.setCellValue(new HSSFRichTextString(cellText));
assertNull(cell.getCellComment());
HSSFPatriarch patr = sheet.createDrawingPatriarch();
HSSFClientAnchor anchor = new HSSFClientAnchor();
anchor.setAnchor( (short)4, 2, 0, 0, (short) 6, 5, 0, 0);
HSSFComment comment = patr.createComment(anchor);
HSSFRichTextString string1 = new HSSFRichTextString(commentText);
comment.setString(string1);
comment.setAuthor(commentAuthor);
cell.setCellComment(comment);
//verify our settings
assertEquals(HSSFSimpleShape.OBJECT_TYPE_COMMENT, comment.getShapeType());
assertEquals(commentAuthor, comment.getAuthor());
assertEquals(commentText, comment.getString().getString());
assertEquals(cellRow, comment.getRow());
assertEquals(cellColumn, comment.getColumn());
//serialize the workbook and read it again
ByteArrayOutputStream out = new ByteArrayOutputStream();
wb.write(out);
out.close();
wb = new HSSFWorkbook(new ByteArrayInputStream(out.toByteArray()));
sheet = wb.getSheetAt(0);
cell = sheet.getRow(cellRow).getCell(cellColumn);
comment = cell.getCellComment();
assertNotNull(comment);
assertEquals(HSSFSimpleShape.OBJECT_TYPE_COMMENT, comment.getShapeType());
assertEquals(commentAuthor, comment.getAuthor());
assertEquals(commentText, comment.getString().getString());
assertEquals(cellRow, comment.getRow());
assertEquals(cellColumn, comment.getColumn());
}
/**
* test that we can read cell comments from an existing workbook.
*/
public static void testReadComments() throws Exception {
String dir = System.getProperty("HSSF.testdata.path");
FileInputStream is = new FileInputStream(new File(dir, "SimpleWithComments.xls"));
HSSFWorkbook wb = new HSSFWorkbook(is);
is.close();
HSSFSheet sheet = wb.getSheetAt(0);
HSSFCell cell;
HSSFRow row;
HSSFComment comment;
for (int rownum = 0; rownum < 3; rownum++) {
row = sheet.getRow(rownum);
cell = row.getCell((short)0);
comment = cell.getCellComment();
assertNull("Cells in the first column are not commented", comment);
}
for (int rownum = 0; rownum < 3; rownum++) {
row = sheet.getRow(rownum);
cell = row.getCell((short)1);
comment = cell.getCellComment();
assertNotNull("Cells in the second column have comments", comment);
assertEquals(HSSFSimpleShape.OBJECT_TYPE_COMMENT, comment.getShapeType());
assertEquals("Yegor Kozlov", comment.getAuthor());
assertFalse("cells in the second column have not empyy notes",
"".equals(comment.getString().getString()));
assertEquals(rownum, comment.getRow());
assertEquals(cell.getCellNum(), comment.getColumn());
}
}
}