<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>
</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>
--- /dev/null
+/* ====================================================================
+ 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();
+
+
+ }
+}
case FilePassRecord.sid:
retval = new FilePassRecord(in);
break;
+ case NoteRecord.sid:
+ retval = new NoteRecord( in );
+ break;
default:
retval = new UnknownRecord( in );
}
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;
/**
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
};
}
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 );
}
--- /dev/null
+/* ====================================================================
+ 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;
+ }
+
+}
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.*;
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;
}
+ // 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() );
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;
}
/**
if ( patriarch != null )
{
shapeToObj.clear();
+ tailRec.clear();
clearEscherRecords();
if ( patriarch.getChildren().size() != 0 )
{
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() );
}
--- /dev/null
+/* ====================================================================
+ 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;
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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;
+ }
+}
+
+
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);
case EndSubRecord.sid:
r = new EndSubRecord( in );
break;
+ case NoteStructureSubRecord.sid:
+ r = new NoteStructureSubRecord( in );
+ break;
default:
r = new UnknownRecord( in );
}
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.
* @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
*/
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
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;
+ }
}
--- /dev/null
+/* ====================================================================
+ 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);
+ }
+}
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.
*/
// 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;
* @see #OBJECT_TYPE_OVAL
* @see #OBJECT_TYPE_RECTANGLE
* @see #OBJECT_TYPE_PICTURE
+ * @see #OBJECT_TYPE_COMMENT
*/
public int getShapeType() { return shapeType; }
* @see #OBJECT_TYPE_OVAL
* @see #OBJECT_TYPE_RECTANGLE
* @see #OBJECT_TYPE_PICTURE
+ * @see #OBJECT_TYPE_COMMENT
*/
public void setShapeType( int shapeType ){ this.shapeType = shapeType; }
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
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;
}
--- /dev/null
+/* ====================================================================
+ 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));
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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);
+
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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());
+ }
+ }
+
+}