From 8b027ef5cdef15ace8ba6585458a32209d9e6928 Mon Sep 17 00:00:00 2001 From: Evgeniy Berlog Date: Wed, 11 Jul 2012 12:08:38 +0000 Subject: [PATCH] checked all examples, added several features git-svn-id: https://svn.apache.org/repos/asf/poi/branches/gsoc2012@1360132 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hssf/record/EscherAggregate.java | 55 +++++++++---- .../poi/hssf/usermodel/EscherGraphics.java | 1 + .../apache/poi/hssf/usermodel/HSSFAnchor.java | 4 +- .../apache/poi/hssf/usermodel/HSSFCell.java | 37 +-------- .../poi/hssf/usermodel/HSSFChildAnchor.java | 20 +++-- .../poi/hssf/usermodel/HSSFClientAnchor.java | 25 +++--- .../poi/hssf/usermodel/HSSFComment.java | 46 +++++------ .../poi/hssf/usermodel/HSSFPatriarch.java | 34 ++++++-- .../poi/hssf/usermodel/HSSFPicture.java | 45 ++++++++++- .../poi/hssf/usermodel/HSSFPolygon.java | 14 ++-- .../apache/poi/hssf/usermodel/HSSFShape.java | 44 +++++++--- .../poi/hssf/usermodel/HSSFShapeFactory.java | 2 +- .../poi/hssf/usermodel/HSSFShapeGroup.java | 30 +++++-- .../apache/poi/hssf/usermodel/HSSFSheet.java | 35 ++++---- .../poi/hssf/usermodel/HSSFSimpleShape.java | 75 +++++++++++++++--- .../poi/hssf/usermodel/HSSFTextbox.java | 70 ++++++---------- .../poi/hssf/usermodel/HSSFUnknownShape.java | 12 ++- .../poi/hssf/usermodel/HSSFWorkbook.java | 2 +- .../poi/hssf/model/TestDrawingShapes.java | 15 +++- .../apache/poi/hssf/model/TestHSSFAnchor.java | 34 ++++++++ .../poi/hssf/usermodel/HSSFTestHelper.java | 2 +- .../poi/hssf/usermodel/TestComment.java | 3 +- .../poi/hssf/usermodel/TestHSSFPicture.java | 61 ++++++++++++++ .../poi/hssf/usermodel/TestPolygon.java | 4 +- test-data/spreadsheet/drawings.xls | Bin 821760 -> 836096 bytes 25 files changed, 455 insertions(+), 215 deletions(-) diff --git a/src/java/org/apache/poi/hssf/record/EscherAggregate.java b/src/java/org/apache/poi/hssf/record/EscherAggregate.java index 11e2c8d4b0..7079cf7dc1 100644 --- a/src/java/org/apache/poi/hssf/record/EscherAggregate.java +++ b/src/java/org/apache/poi/hssf/record/EscherAggregate.java @@ -303,6 +303,12 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { protected HSSFPatriarch patriarch; + /** + * if we want to get the same byte array if we open existing file and serialize it we should save + * note records in right order. This list contains ids of NoteRecords in such order as we read from existing file + */ + private List _tailIds = new ArrayList(); + /** * Maps shape container objects to their {@link TextObjectRecord} or {@link ObjRecord} */ @@ -313,7 +319,7 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { /** * list of "tail" records that need to be serialized after all drawing group records */ - private List tailRec = new ArrayList(); + private Map tailRec = new HashMap(); public EscherAggregate() { buildBaseTree(); @@ -440,7 +446,8 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { while (loc < records.size()) { if (sid(records, loc) == NoteRecord.sid) { NoteRecord r = (NoteRecord) records.get(loc); - agg.tailRec.add(r); + agg.tailRec.put(r.getShapeId(), r); + agg._tailIds.add(agg._tailIds.size(), r.getShapeId()); } else { break; } @@ -531,8 +538,18 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { } // write records that need to be serialized after all drawing group records - for (i = 0; i < tailRec.size(); i++) { - Record rec = tailRec.get(i); + Map tailCopy = new HashMap(tailRec); + // at first we should save records in correct order which were already in the file during EscherAggregate.createAggregate() + for (Integer id : _tailIds){ + NoteRecord note = tailCopy.get(id); + if (null != note){ + pos += note.serialize(pos, data); + tailCopy.remove(id); + } + } + // Add all other notes which were created after createAggregate() + for (i = 0; i < tailCopy.size(); i++) { + Record rec = (Record) tailCopy.values().toArray()[i]; pos += rec.serialize(pos, data); } int bytesWritten = pos - offset; @@ -622,7 +639,7 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { objRecordSize += r.getRecordSize(); } int tailRecordSize = 0; - for (Iterator iterator = tailRec.iterator(); iterator.hasNext(); ) { + for (Iterator iterator = tailRec.values().iterator(); iterator.hasNext(); ) { Record r = (Record) iterator.next(); tailRecordSize += r.getRecordSize(); } @@ -636,6 +653,10 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { return shapeToObj.put(r, objRecord); } + public void removeShapeToObjRecord(EscherRecord rec){ + shapeToObj.remove(rec); + } + public HSSFPatriarch getPatriarch() { return patriarch; } @@ -938,7 +959,7 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { if (shapeModel instanceof CommentShape) { CommentShape comment = (CommentShape) shapeModel; - tailRec.add(comment.getNoteRecord()); + tailRec.put(comment.getNoteRecord().getShapeId(), comment.getNoteRecord()); } } @@ -1058,7 +1079,9 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { spgr.setRectX2(1023); spgr.setRectY2(255); sp1.setRecordId(EscherSpRecord.RECORD_ID); + sp1.setOptions((short) 0x0002); + sp1.setVersion((short) 0x2); sp1.setShapeId(-1); sp1.setFlags(EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_PATRIARCH); dgContainer.addChildRecord(dg); @@ -1161,22 +1184,20 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { * @return tails records. We need to access them when building shapes. * Every HSSFComment shape has a link to a NoteRecord from the tailRec collection. */ - public List getTailRecords() { - return Collections.unmodifiableList(tailRec); + public Map getTailRecords() { + return tailRec; } public NoteRecord getNoteRecordByObj(ObjRecord obj) { - for (Record rec : tailRec) { - NoteRecord note = (NoteRecord) rec; - CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) obj.getSubRecords().get(0); - if (note.getShapeId() == cod.getObjectId()) { - return note; - } - } - return null; + CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) obj.getSubRecords().get(0); + return tailRec.get(cod.getObjectId()); } public void addTailRecord(NoteRecord note){ - tailRec.add(note); + tailRec.put(note.getShapeId(), note); + } + + public void removeTailRecord(NoteRecord note){ + tailRec.remove(note.getShapeId()); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java index 7500ac7b7b..90a93e5260 100644 --- a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java +++ b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java @@ -349,6 +349,7 @@ public class EscherGraphics shape.setLineStyle(HSSFShape.LINESTYLE_NONE); shape.setFillColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); + shape.setNoFill(false); } /** diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java index 2fe36130cf..32422168b5 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java @@ -31,6 +31,9 @@ import org.apache.poi.ddf.EscherRecord; */ public abstract class HSSFAnchor { + protected boolean _isHorizontallyFlipped = false; + protected boolean _isVerticallyFlipped = false; + public HSSFAnchor() { createEscherAnchor(); } @@ -51,7 +54,6 @@ public abstract class HSSFAnchor { return new HSSFClientAnchor((EscherClientAnchorRecord) container.getChildById(EscherClientAnchorRecord.RECORD_ID)); } return null; -// throw new IllegalArgumentException("continer must have anchor record"); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java index 28c15fac72..67fb64f227 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java @@ -1021,7 +1021,7 @@ public class HSSFCell implements Cell { */ public HSSFComment getCellComment(){ if (_comment == null) { - _comment = findCellComment(_sheet.getSheet(), _record.getRow(), _record.getColumn()); + _comment = _sheet.findCellComment(_record.getRow(), _record.getColumn()); } return _comment; } @@ -1033,41 +1033,12 @@ public class HSSFCell implements Cell { * all comments after performing this action! */ public void removeCellComment() { - HSSFComment comment = findCellComment(_sheet.getSheet(), _record.getRow(), _record.getColumn()); + HSSFComment comment = _sheet.findCellComment(_record.getRow(), _record.getColumn()); _comment = null; - - if(comment == null) { - // Nothing to do + if (null == comment){ return; } - - // Zap the underlying NoteRecord - List sheetRecords = _sheet.getSheet().getRecords(); - sheetRecords.remove(comment.getNoteRecord()); - - // If we have a TextObjectRecord, is should - // be proceeed by: - // MSODRAWING with container - // OBJ - // MSODRAWING with EscherTextboxRecord - if(comment.getTextObjectRecord() != null) { - TextObjectRecord txo = comment.getTextObjectRecord(); - int txoAt = sheetRecords.indexOf(txo); - - if(sheetRecords.get(txoAt-3) instanceof DrawingRecord && - sheetRecords.get(txoAt-2) instanceof ObjRecord && - sheetRecords.get(txoAt-1) instanceof DrawingRecord) { - // Zap these, in reverse order - sheetRecords.remove(txoAt-1); - sheetRecords.remove(txoAt-2); - sheetRecords.remove(txoAt-3); - } else { - throw new IllegalStateException("Found the wrong records before the TextObjectRecord, can't remove comment"); - } - - // Now remove the text record - sheetRecords.remove(txo); - } + _sheet.getDrawingPatriarch().removeShape(comment); } /** diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java index 38c4f5f0e2..9df47d64c1 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java @@ -34,7 +34,13 @@ public final class HSSFChildAnchor extends HSSFAnchor { } public HSSFChildAnchor(int dx1, int dy1, int dx2, int dy2) { - super(dx1, dy1, dx2, dy2); + super(Math.min(dx1, dx2), Math.min(dy1, dy2), Math.max(dx1, dx2), Math.max(dy1, dy2)); + if (dx1 > dx2){ + _isHorizontallyFlipped = true; + } + if (dy1 > dy2){ + _isVerticallyFlipped = true; + } } @Override @@ -78,18 +84,18 @@ public final class HSSFChildAnchor extends HSSFAnchor { } public void setAnchor(int dx1, int dy1, int dx2, int dy2) { - setDx1(dx1); - setDy1(dy1); - setDx2(dx2); - setDy2(dy2); + setDx1(Math.min(dx1, dx2)); + setDy1(Math.min(dy1, dy2)); + setDx2(Math.max(dx1, dx2)); + setDy2(Math.max(dy1, dy2)); } public boolean isHorizontallyFlipped() { - return getDx1() > getDx2(); + return _isHorizontallyFlipped; } public boolean isVerticallyFlipped() { - return getDy1() > getDy2(); + return _isVerticallyFlipped; } @Override diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java index 49e20a42f1..ad77c7a4d5 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java @@ -66,10 +66,17 @@ public final class HSSFClientAnchor extends HSSFAnchor implements ClientAnchor { checkRange(row1, 0, 255 * 256, "row1"); checkRange(row2, 0, 255 * 256, "row2"); - setCol1(col1); - setCol2(col2); - setRow1(row1); - setRow2(row2); + setCol1((short) Math.min(col1, col2)); + setCol2((short) Math.max(col1, col2)); + setRow1((short) Math.min(row1, row2)); + setRow2((short) Math.max(row1, row2)); + + if (col1 > col2){ + _isHorizontallyFlipped = true; + } + if (row1 > row2){ + _isVerticallyFlipped = true; + } } /** @@ -187,20 +194,14 @@ public final class HSSFClientAnchor extends HSSFAnchor implements ClientAnchor { * @return true if the anchor goes from right to left. */ public boolean isHorizontallyFlipped() { - if (getCol1() == getCol2()) { - return getDx1() > getDx2(); - } - return getCol1() > getCol2(); + return _isHorizontallyFlipped; } /** * @return true if the anchor goes from bottom to top. */ public boolean isVerticallyFlipped() { - if (getRow1() == getRow2()) { - return getDy1() > getDy2(); - } - return getRow1() > getRow2(); + return _isVerticallyFlipped; } @Override diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java b/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java index 3e844516e9..ea8ae3c7e7 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFComment.java @@ -16,7 +16,7 @@ ==================================================================== */ package org.apache.poi.hssf.usermodel; -import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.*; import org.apache.poi.hssf.record.*; import org.apache.poi.ss.usermodel.Comment; import org.apache.poi.ss.usermodel.RichTextString; @@ -64,7 +64,6 @@ public class HSSFComment extends HSSFTextbox implements Comment { protected HSSFComment(NoteRecord note, TextObjectRecord txo) { this(null, new HSSFClientAnchor()); - _textObjectRecord = txo; _note = note; } @@ -74,6 +73,17 @@ public class HSSFComment extends HSSFTextbox implements Comment { _patriarch._getBoundAggregate().addTailRecord(getNoteRecord()); } + @Override + protected EscherContainerRecord createSpContainer() { + EscherContainerRecord spContainer = super.createSpContainer(); + EscherOptRecord opt = spContainer.getChildById(EscherOptRecord.RECORD_ID); + removeEscherProperty(opt, EscherProperties.TEXT__TEXTLEFT); + removeEscherProperty(opt, EscherProperties.TEXT__TEXTRIGHT); + removeEscherProperty(opt, EscherProperties.TEXT__TEXTTOP); + removeEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM); + return spContainer; + } + @Override protected ObjRecord createObjRecord() { ObjRecord obj = new ObjRecord(); @@ -94,7 +104,7 @@ public class HSSFComment extends HSSFTextbox implements Comment { private NoteRecord createNoteRecord(){ NoteRecord note = new NoteRecord(); - note.setFlags(NoteRecord.NOTE_VISIBLE); + note.setFlags(NoteRecord.NOTE_HIDDEN); note.setAuthor(""); return note; } @@ -102,7 +112,7 @@ public class HSSFComment extends HSSFTextbox implements Comment { @Override void setShapeId(int shapeId) { super.setShapeId(shapeId); - CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) _objRecord.getSubRecords().get(0); + CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) getObjRecord().getSubRecords().get(0); cod.setObjectId((short) (shapeId)); _note.setShapeId(shapeId); } @@ -187,20 +197,6 @@ public class HSSFComment extends HSSFTextbox implements Comment { if (_note != null) _note.setAuthor(author); } - /** - * Sets the rich text string used by this comment. - * - * @param string Sets the rich text string used by this object. - */ - public void setString(RichTextString string) { - HSSFRichTextString hstring = (HSSFRichTextString) string; - //if font is not set we must set the default one - if (hstring.numFormattingRuns() == 0) hstring.applyFont((short) 0); - - _textObjectRecord.setStr(hstring); - super.setString(string); - } - /** * Returns the underlying Note record */ @@ -208,10 +204,14 @@ public class HSSFComment extends HSSFTextbox implements Comment { return _note; } - /** - * Returns the underlying Text record - */ - public TextObjectRecord getTextObjectRecord() { - return _textObjectRecord; + @Override + public void setShapeType(int shapeType) { + throw new IllegalStateException("Shape type can not be changed in "+this.getClass().getSimpleName()); + } + + public void afterRemove(HSSFPatriarch patriarch){ + patriarch._getBoundAggregate().removeShapeToObjRecord(getEscherContainer().getChildById(EscherClientDataRecord.RECORD_ID)); + patriarch._getBoundAggregate().removeShapeToObjRecord(getEscherContainer().getChildById(EscherTextboxRecord.RECORD_ID)); + patriarch._getBoundAggregate().removeTailRecord(getNoteRecord()); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java index 07fcc4d0d0..aa8603635b 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java @@ -44,6 +44,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing { // private int _y2 = 255; private final EscherSpgrRecord _spgrRecord; + private final EscherContainerRecord _mainSpgrContainer; /** * The EscherAggregate we have been bound to. @@ -61,12 +62,23 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing { HSSFPatriarch(HSSFSheet sheet, EscherAggregate boundAggregate){ _sheet = sheet; _boundAggregate = boundAggregate; + _mainSpgrContainer = _boundAggregate.getEscherContainer().getChildContainers().get(0); EscherContainerRecord spContainer = (EscherContainerRecord) _boundAggregate.getEscherContainer() .getChildContainers().get(0).getChild(0); _spgrRecord = spContainer.getChildById(EscherSpgrRecord.RECORD_ID); buildShapeTree(); } + /** + * remove first level shapes + * @param shape to be removed + */ + public void removeShape(HSSFShape shape){ + _mainSpgrContainer.removeChildRecord(shape.getEscherContainer()); + shape.afterRemove(this); + _shapes.remove(shape); + } + public void afterCreate(){ DrawingManager2 drawingManager = _sheet.getWorkbook().getWorkbook().getDrawingManager(); short dgId = drawingManager.findNewDrawingGroupId(); @@ -106,6 +118,13 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing { addShape(shape); //open existing file onCreate(shape); + EscherSpRecord sp = shape.getEscherContainer().getChildById(EscherSpRecord.RECORD_ID); + if (shape.anchor.isHorizontallyFlipped()){ + sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPHORIZ); + } + if (shape.anchor.isVerticallyFlipped()){ + sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPVERT); + } return shape; } @@ -125,8 +144,13 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing { //open existing file onCreate(shape); - EscherBSERecord bse = _sheet.getWorkbook().getWorkbook().getBSERecord(pictureIndex); - bse.setRef(bse.getRef() + 1); + EscherSpRecord sp = shape.getEscherContainer().getChildById(EscherSpRecord.RECORD_ID); + if (shape.anchor.isHorizontallyFlipped()){ + sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPHORIZ); + } + if (shape.anchor.isVerticallyFlipped()){ + sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPVERT); + } return shape; } @@ -368,11 +392,7 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing { for(int i = 0; i < spgrChildren.size(); i++){ EscherContainerRecord spContainer = spgrChildren.get(i); if (i == 0){ - EscherSpgrRecord spgr = (EscherSpgrRecord)spContainer.getChildById(EscherSpgrRecord.RECORD_ID); - setCoordinates( - spgr.getRectX1(), spgr.getRectY1(), - spgr.getRectX2(), spgr.getRectY2() - ); + continue; } else { HSSFShapeFactory.createShapeTree(spContainer, _boundAggregate, this); } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java index 985bb8f177..9eff50dd3e 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPicture.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel; import java.awt.Dimension; import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; import org.apache.poi.ddf.*; import org.apache.poi.hssf.record.ObjRecord; @@ -69,7 +70,7 @@ public final class HSSFPicture extends HSSFSimpleShape implements Picture { public int getPictureIndex() { - EscherSimpleProperty property = _optRecord.lookup(EscherProperties.BLIP__BLIPTODISPLAY); + EscherSimpleProperty property = getOptRecord().lookup(EscherProperties.BLIP__BLIPTODISPLAY); if (null == property){ return -1; } @@ -81,6 +82,15 @@ public final class HSSFPicture extends HSSFSimpleShape implements Picture { setPropertyValue(new EscherSimpleProperty( EscherProperties.BLIP__BLIPTODISPLAY, false, true, pictureIndex)); } + @Override + protected EscherContainerRecord createSpContainer() { + EscherContainerRecord spContainer = super.createSpContainer(); + EscherOptRecord opt = spContainer.getChildById(EscherOptRecord.RECORD_ID); + removeEscherProperty(opt, EscherProperties.LINESTYLE__LINEDASHING); + removeEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH); + return spContainer; + } + /** * Resize the image *

@@ -237,4 +247,37 @@ public final class HSSFPicture extends HSSFSimpleShape implements Picture { EscherBlipRecord blipRecord = iwb.getBSERecord(getPictureIndex()).getBlipRecord(); return new HSSFPictureData(blipRecord); } + + @Override + void afterInsert(HSSFPatriarch patriarch) { + super.afterInsert(patriarch); + EscherBSERecord bse = + patriarch._sheet.getWorkbook().getWorkbook().getBSERecord(getPictureIndex()); + bse.setRef(bse.getRef() + 1); + } + + /** + * The color applied to the lines of this shape. + */ + public String getAdditionalData() { + EscherComplexProperty propFile = (EscherComplexProperty) getOptRecord().lookup( + EscherProperties.BLIP__BLIPFILENAME); + try { + if (null == propFile){ + return ""; + } + return new String(propFile.getComplexData(), "UTF-16LE").trim(); + } catch (UnsupportedEncodingException e) { + return ""; + } + } + + public void setAdditionalData(String data){ + try { + EscherComplexProperty prop = new EscherComplexProperty(EscherProperties.BLIP__BLIPFILENAME, true, data.getBytes("UTF-16LE")); + setPropertyValue(prop); + } catch (UnsupportedEncodingException e) { + System.out.println("Unsupported encoding: UTF-16LE"); + } + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java index 44684a6052..079ae744ae 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java @@ -106,8 +106,12 @@ public class HSSFPolygon extends HSSFShape { return obj; } + @Override + protected void afterRemove(HSSFPatriarch patriarch) { + } + public int[] getXPoints() { - EscherArrayProperty verticesProp = _optRecord.lookup(EscherProperties.GEOMETRY__VERTICES); + EscherArrayProperty verticesProp = getOptRecord().lookup(EscherProperties.GEOMETRY__VERTICES); if (null == verticesProp){ return new int[]{}; } @@ -121,7 +125,7 @@ public class HSSFPolygon extends HSSFShape { } public int[] getYPoints() { - EscherArrayProperty verticesProp = _optRecord.lookup(EscherProperties.GEOMETRY__VERTICES); + EscherArrayProperty verticesProp = getOptRecord().lookup(EscherProperties.GEOMETRY__VERTICES); if (null == verticesProp){ return new int[]{}; } @@ -188,18 +192,18 @@ public class HSSFPolygon extends HSSFShape { } public int getDrawAreaWidth() { - EscherSimpleProperty property = _optRecord.lookup(EscherProperties.GEOMETRY__RIGHT); + EscherSimpleProperty property = getOptRecord().lookup(EscherProperties.GEOMETRY__RIGHT); return property == null ? 100: property.getPropertyValue(); } public int getDrawAreaHeight() { - EscherSimpleProperty property = _optRecord.lookup(EscherProperties.GEOMETRY__BOTTOM); + EscherSimpleProperty property = getOptRecord().lookup(EscherProperties.GEOMETRY__BOTTOM); return property == null ? 100: property.getPropertyValue(); } @Override void afterInsert(HSSFPatriarch patriarch) { EscherAggregate agg = patriarch._getBoundAggregate(); - agg.associateShapeToObjRecord(_escherContainer.getChildById(EscherClientDataRecord.RECORD_ID), getObjRecord()); + agg.associateShapeToObjRecord(getEscherContainer().getChildById(EscherClientDataRecord.RECORD_ID), getObjRecord()); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java index be69ad6354..4e01aa63ea 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java @@ -21,6 +21,8 @@ import org.apache.poi.ddf.*; import org.apache.poi.hssf.record.CommonObjectDataSubRecord; import org.apache.poi.hssf.record.ObjRecord; +import java.util.Iterator; + /** * An abstract shape. * @@ -53,9 +55,12 @@ public abstract class HSSFShape { HSSFAnchor anchor; HSSFPatriarch _patriarch; - protected final EscherContainerRecord _escherContainer; - protected final ObjRecord _objRecord; - protected final EscherOptRecord _optRecord; + private final EscherContainerRecord _escherContainer; + private final ObjRecord _objRecord; + private final EscherOptRecord _optRecord; + + public final static int NO_FILLHITTEST_TRUE = 0x00110000; + public final static int NO_FILLHITTEST_FALSE = 0x00010000; public HSSFShape(EscherContainerRecord spContainer, ObjRecord objRecord) { this._escherContainer = spContainer; @@ -80,6 +85,17 @@ public abstract class HSSFShape { protected abstract ObjRecord createObjRecord(); + protected abstract void afterRemove(HSSFPatriarch patriarch); + + protected void removeEscherProperty(EscherOptRecord opt, int num){ + for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); ) { + EscherProperty prop = iterator.next(); + if (prop.getPropertyNumber() == num){ + iterator.remove(); + } + } + } + void setShapeId(int shapeId){ EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); spRecord.setShapeId(shapeId); @@ -91,8 +107,7 @@ public abstract class HSSFShape { return ((EscherSpRecord)_escherContainer.getChildById(EscherSpRecord.RECORD_ID)).getShapeId(); } - void afterInsert(HSSFPatriarch patriarch){ - } + abstract void afterInsert(HSSFPatriarch patriarch); public EscherContainerRecord getEscherContainer() { return _escherContainer; @@ -102,6 +117,10 @@ public abstract class HSSFShape { return _objRecord; } + public EscherOptRecord getOptRecord() { + return _optRecord; + } + /** * Gets the parent shape. */ @@ -248,10 +267,13 @@ public abstract class HSSFShape { */ public void setLineStyle(int lineStyle) { setPropertyValue(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINEDASHING, lineStyle)); - if (getLineStyle() == HSSFShape.LINESTYLE_NONE){ - setPropertyValue(new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080000)); - } else { - setPropertyValue( new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080008)); + if (getLineStyle() != HSSFShape.LINESTYLE_SOLID) { + setPropertyValue(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINEENDCAPSTYLE, 0)); + if (getLineStyle() == HSSFShape.LINESTYLE_NONE){ + setPropertyValue(new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080000)); + } else { + setPropertyValue( new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080008)); + } } } @@ -260,14 +282,14 @@ public abstract class HSSFShape { */ public boolean isNoFill() { EscherBoolProperty property = _optRecord.lookup(EscherProperties.FILL__NOFILLHITTEST); - return property == null ? NO_FILL_DEFAULT : property.isTrue(); + return property == null ? NO_FILL_DEFAULT : property.getPropertyValue() == NO_FILLHITTEST_TRUE; } /** * Sets whether this shape is filled or transparent. */ public void setNoFill(boolean noFill) { - setPropertyValue(new EscherBoolProperty(EscherProperties.FILL__NOFILLHITTEST, noFill ? 1 : 0)); + setPropertyValue(new EscherBoolProperty(EscherProperties.FILL__NOFILLHITTEST, noFill ? NO_FILLHITTEST_TRUE : NO_FILLHITTEST_FALSE)); } protected void setPropertyValue(EscherProperty property){ diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java index e65afcb200..eab30fcdaa 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java @@ -117,7 +117,7 @@ public class HSSFShapeFactory { shape = new HSSFPicture(container, objRecord); break; case CommonObjectDataSubRecord.OBJECT_TYPE_RECTANGLE: - shape = new HSSFSimpleShape(container, objRecord); + shape = new HSSFSimpleShape(container, objRecord, txtRecord); break; case CommonObjectDataSubRecord.OBJECT_TYPE_LINE: shape = new HSSFSimpleShape(container, objRecord); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java index f7fab2bbc8..32471f5176 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java @@ -57,7 +57,7 @@ public class HSSFShapeGroup extends HSSFShape implements HSSFShapeContainer { public HSSFShapeGroup(HSSFShape parent, HSSFAnchor anchor) { super(parent, anchor); - _spgrRecord = ((EscherContainerRecord)_escherContainer.getChild(0)).getChildById(EscherSpgrRecord.RECORD_ID); + _spgrRecord = ((EscherContainerRecord)getEscherContainer().getChild(0)).getChildById(EscherSpgrRecord.RECORD_ID); } @Override @@ -122,12 +122,16 @@ public class HSSFShapeGroup extends HSSFShape implements HSSFShapeContainer { return obj; } + @Override + protected void afterRemove(HSSFPatriarch patriarch) { + } + private void onCreate(HSSFShape shape){ if(_patriarch != null && _patriarch._getBoundAggregate().getPatriarch() == null){ EscherContainerRecord spContainer = shape.getEscherContainer(); int shapeId = _patriarch.newShapeId(); shape.setShapeId(shapeId); - _escherContainer.addChildRecord(spContainer); + getEscherContainer().addChildRecord(spContainer); shape.afterInsert(_patriarch); EscherSpRecord sp = shape.getEscherContainer().getChildById(EscherSpRecord.RECORD_ID); sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_CHILD); @@ -172,6 +176,13 @@ public class HSSFShapeGroup extends HSSFShape implements HSSFShapeContainer { shape.anchor = anchor; shapes.add(shape); onCreate(shape); + EscherSpRecord sp = shape.getEscherContainer().getChildById(EscherSpRecord.RECORD_ID); + if (shape.anchor.isHorizontallyFlipped()){ + sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPHORIZ); + } + if (shape.anchor.isVerticallyFlipped()){ + sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPVERT); + } return shape; } @@ -220,6 +231,13 @@ public class HSSFShapeGroup extends HSSFShape implements HSSFShapeContainer { shape.setPictureIndex(pictureIndex); shapes.add(shape); onCreate(shape); + EscherSpRecord sp = shape.getEscherContainer().getChildById(EscherSpRecord.RECORD_ID); + if (shape.anchor.isHorizontallyFlipped()){ + sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPHORIZ); + } + if (shape.anchor.isVerticallyFlipped()){ + sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPVERT); + } return shape; } @@ -284,22 +302,22 @@ public class HSSFShapeGroup extends HSSFShape implements HSSFShapeContainer { @Override void afterInsert(HSSFPatriarch patriarch){ EscherAggregate agg = patriarch._getBoundAggregate(); - EscherContainerRecord containerRecord = _escherContainer.getChildById(EscherContainerRecord.SP_CONTAINER); + EscherContainerRecord containerRecord = getEscherContainer().getChildById(EscherContainerRecord.SP_CONTAINER); agg.associateShapeToObjRecord(containerRecord.getChildById(EscherClientDataRecord.RECORD_ID), getObjRecord()); } @Override void setShapeId(int shapeId){ - EscherContainerRecord containerRecord = _escherContainer.getChildById(EscherContainerRecord.SP_CONTAINER); + EscherContainerRecord containerRecord = getEscherContainer().getChildById(EscherContainerRecord.SP_CONTAINER); EscherSpRecord spRecord = containerRecord.getChildById(EscherSpRecord.RECORD_ID); spRecord.setShapeId(shapeId); - CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) _objRecord.getSubRecords().get(0); + CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) getObjRecord().getSubRecords().get(0); cod.setObjectId((short) (shapeId)); } @Override int getShapeId(){ - EscherContainerRecord containerRecord = _escherContainer.getChildById(EscherContainerRecord.SP_CONTAINER); + EscherContainerRecord containerRecord = getEscherContainer().getChildById(EscherContainerRecord.SP_CONTAINER); return ((EscherSpRecord)containerRecord.getChildById(EscherSpRecord.RECORD_ID)).getShapeId(); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index aa25061c40..f294946008 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -1358,15 +1358,17 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { // exist for cells which are null if(moveComments) { // This code would get simpler if NoteRecords could be organised by HSSFRow. - for(int i=noteRecs.length-1; i>=0; i--) { - NoteRecord nr = noteRecs[i]; - if (nr.getRow() != rowNum) { + HSSFPatriarch patriarch = createDrawingPatriarch(); + for(int i=patriarch.getChildren().size()-1; i>=0; i--){ + HSSFShape shape = patriarch.getChildren().get(i); + if (!(shape instanceof HSSFComment)){ continue; } - HSSFComment comment = getCellComment(rowNum, nr.getColumn()); - if (comment != null) { - comment.setRow(rowNum + n); + HSSFComment comment = (HSSFComment) shape; + if (comment.getRow() != rowNum){ + continue; } + comment.setRow(rowNum + n); } } } @@ -1875,20 +1877,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { * @return cell comment or null if not found */ public HSSFComment getCellComment(int row, int column) { - // Don't call findCellComment directly, otherwise - // two calls to this method will result in two - // new HSSFComment instances, which is bad - HSSFRow r = getRow(row); - if(r != null) { - HSSFCell c = r.getCell(column); - if(c != null) { - return c.getCellComment(); - } - // No cell, so you will get new - // objects every time, sorry... - return HSSFCell.findCellComment(_sheet, row, column); - } - return null; + return findCellComment(row, column); } public HSSFSheetConditionalFormatting getSheetConditionalFormatting() { @@ -2009,7 +1998,11 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { } protected HSSFComment findCellComment(int row, int column) { - return lookForComment(getDrawingPatriarch(), row, column); + HSSFPatriarch patriarch = getDrawingPatriarch(); + if (null == patriarch){ + patriarch = createDrawingPatriarch(); + } + return lookForComment(patriarch, row, column); } private HSSFComment lookForComment(HSSFShapeContainer container, int row, int column){ diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java index bc000b715b..ac4697ac14 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java @@ -18,11 +18,9 @@ package org.apache.poi.hssf.usermodel; import org.apache.poi.ddf.*; -import org.apache.poi.hssf.record.CommonObjectDataSubRecord; -import org.apache.poi.hssf.record.EndSubRecord; -import org.apache.poi.hssf.record.EscherAggregate; -import org.apache.poi.hssf.record.ObjRecord; +import org.apache.poi.hssf.record.*; import org.apache.poi.hssf.usermodel.drawing.HSSFShapeType; +import org.apache.poi.ss.usermodel.RichTextString; import java.util.HashMap; import java.util.Map; @@ -61,6 +59,8 @@ public class HSSFSimpleShape extends HSSFShape private static final Map objTypeToShapeType = new HashMap(); + private TextObjectRecord _textObjectRecord; + static { objTypeToShapeType.put(OBJECT_TYPE_RECTANGLE, HSSFShapeType.RECTANGLE.getType()); objTypeToShapeType.put(OBJECT_TYPE_PICTURE, HSSFShapeType.PICTURE.getType()); @@ -68,6 +68,11 @@ public class HSSFSimpleShape extends HSSFShape objTypeToShapeType.put(OBJECT_TYPE_OVAL, HSSFShapeType.OVAL.getType()); } + public HSSFSimpleShape(EscherContainerRecord spContainer, ObjRecord objRecord, TextObjectRecord _textObjectRecord) { + super(spContainer, objRecord); + this._textObjectRecord = _textObjectRecord; + } + public HSSFSimpleShape(EscherContainerRecord spContainer, ObjRecord objRecord) { super(spContainer, objRecord); } @@ -75,7 +80,18 @@ public class HSSFSimpleShape extends HSSFShape public HSSFSimpleShape( HSSFShape parent, HSSFAnchor anchor) { super( parent, anchor ); - setShapeType(OBJECT_TYPE_LINE); + _textObjectRecord = createTextObjRecord(); + } + + public TextObjectRecord getTextObjectRecord() { + return _textObjectRecord; + } + + protected TextObjectRecord createTextObjRecord(){ + TextObjectRecord obj = new TextObjectRecord(); + obj.setTextLocked(true); + obj.setTextOrientation(TextObjectRecord.TEXT_ORIENTATION_NONE); + return obj; } @Override @@ -87,10 +103,11 @@ public class HSSFSimpleShape extends HSSFShape EscherSpRecord sp = new EscherSpRecord(); sp.setRecordId( EscherSpRecord.RECORD_ID ); sp.setFlags( EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE ); + sp.setVersion((short) 0x2); EscherClientDataRecord clientData = new EscherClientDataRecord(); clientData.setRecordId( EscherClientDataRecord.RECORD_ID ); - clientData.setOptions( (short) 0x0000 ); + clientData.setOptions( (short) (0x0000) ); EscherOptRecord optRecord = new EscherOptRecord(); optRecord.setEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINEDASHING, LINESTYLE_SOLID)); @@ -98,7 +115,7 @@ public class HSSFSimpleShape extends HSSFShape // optRecord.setEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINEWIDTH, LINEWIDTH_DEFAULT)); optRecord.setEscherProperty(new EscherRGBProperty(EscherProperties.FILL__FILLCOLOR, FILL__FILLCOLOR_DEFAULT)); optRecord.setEscherProperty(new EscherRGBProperty(EscherProperties.LINESTYLE__COLOR, LINESTYLE__COLOR_DEFAULT)); - optRecord.setEscherProperty(new EscherBoolProperty(EscherProperties.FILL__NOFILLHITTEST, 0)); + optRecord.setEscherProperty(new EscherBoolProperty(EscherProperties.FILL__NOFILLHITTEST, NO_FILLHITTEST_FALSE)); optRecord.setEscherProperty( new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080008)); optRecord.setEscherProperty( new EscherShapePathProperty( EscherProperties.GEOMETRY__SHAPEPATH, EscherShapePathProperty.COMPLEX ) ); @@ -127,10 +144,46 @@ public class HSSFSimpleShape extends HSSFShape return obj; } + @Override + protected void afterRemove(HSSFPatriarch patriarch) { + } + + /** + * @return the rich text string for this textbox. + */ + public HSSFRichTextString getString() { + return _textObjectRecord.getStr(); + } + + /** + * @param string Sets the rich text string used by this object. + */ + public void setString(RichTextString string) { + HSSFRichTextString rtr = (HSSFRichTextString) string; + // If font is not set we must set the default one + if (rtr.numFormattingRuns() == 0) rtr.applyFont((short) 0); + EscherTextboxRecord textbox = getEscherContainer().getChildById(EscherTextboxRecord.RECORD_ID); + if (string.getString()!= null && !string.getString().equals("")){ + if (null == textbox){ + EscherTextboxRecord escherTextbox = new EscherTextboxRecord(); + escherTextbox.setRecordId(EscherTextboxRecord.RECORD_ID); + escherTextbox.setOptions((short) 0x0000); + getEscherContainer().addChildRecord(escherTextbox); + _patriarch._getBoundAggregate().associateShapeToObjRecord(getEscherContainer().getChildById(EscherTextboxRecord.RECORD_ID), getTextObjectRecord()); + } + } else { + if (null != textbox){ + getEscherContainer().removeChildRecord(textbox); + _patriarch._getBoundAggregate().removeShapeToObjRecord(textbox); + } + } + _textObjectRecord.setStr(rtr); + } + @Override void afterInsert(HSSFPatriarch patriarch){ EscherAggregate agg = patriarch._getBoundAggregate(); - agg.associateShapeToObjRecord(_escherContainer.getChildById(EscherClientDataRecord.RECORD_ID), getObjRecord()); + agg.associateShapeToObjRecord(getEscherContainer().getChildById(EscherClientDataRecord.RECORD_ID), getObjRecord()); } @@ -145,7 +198,7 @@ public class HSSFSimpleShape extends HSSFShape * @see #OBJECT_TYPE_COMMENT */ public int getShapeType() { - CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) _objRecord.getSubRecords().get(0); + CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) getObjRecord().getSubRecords().get(0); return cod.getObjectType(); } @@ -161,9 +214,9 @@ public class HSSFSimpleShape extends HSSFShape * @see #OBJECT_TYPE_COMMENT */ public void setShapeType( int shapeType ){ - CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) _objRecord.getSubRecords().get(0); + CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) getObjRecord().getSubRecords().get(0); cod.setObjectType((short) shapeType); - EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + EscherSpRecord spRecord = getEscherContainer().getChildById(EscherSpRecord.RECORD_ID); if (null == objTypeToShapeType.get((short)shapeType)){ System.out.println("Unknown shape type: "+shapeType); return; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java b/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java index 4e652de5e5..af39d59784 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java @@ -47,11 +47,8 @@ public class HSSFTextbox extends HSSFSimpleShape { public final static short VERTICAL_ALIGNMENT_JUSTIFY = 4; public final static short VERTICAL_ALIGNMENT_DISTRIBUTED = 7; - protected TextObjectRecord _textObjectRecord; - public HSSFTextbox(EscherContainerRecord spContainer, ObjRecord objRecord, TextObjectRecord textObjectRecord) { - super(spContainer, objRecord); - this._textObjectRecord = textObjectRecord; + super(spContainer, objRecord, textObjectRecord); } HSSFRichTextString string = new HSSFRichTextString(""); @@ -64,25 +61,16 @@ public class HSSFTextbox extends HSSFSimpleShape { */ public HSSFTextbox(HSSFShape parent, HSSFAnchor anchor) { super(parent, anchor); - _textObjectRecord = createTextObjRecord(); setHorizontalAlignment(HORIZONTAL_ALIGNMENT_LEFT); setVerticalAlignment(VERTICAL_ALIGNMENT_TOP); setString(new HSSFRichTextString("")); - - } - - protected TextObjectRecord createTextObjRecord(){ - TextObjectRecord obj = new TextObjectRecord(); - obj.setTextLocked(true); - obj.setTextOrientation(TextObjectRecord.TEXT_ORIENTATION_NONE); - return obj; } @Override protected ObjRecord createObjRecord() { ObjRecord obj = new ObjRecord(); CommonObjectDataSubRecord c = new CommonObjectDataSubRecord(); - c.setObjectType(OBJECT_TYPE_TEXT); + c.setObjectType(HSSFTextbox.OBJECT_TYPE_TEXT); c.setLocked( true ); c.setPrintable( true ); c.setAutofill( true ); @@ -90,8 +78,7 @@ public class HSSFTextbox extends HSSFSimpleShape { EndSubRecord e = new EndSubRecord(); obj.addSubRecord( c ); obj.addSubRecord( e ); - return obj; - } + return obj; } @Override protected EscherContainerRecord createSpContainer() { @@ -123,7 +110,7 @@ public class HSSFTextbox extends HSSFSimpleShape { opt.setEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINEWIDTH, LINEWIDTH_DEFAULT)); opt.setEscherProperty(new EscherRGBProperty(EscherProperties.FILL__FILLCOLOR, FILL__FILLCOLOR_DEFAULT)); opt.setEscherProperty(new EscherRGBProperty(EscherProperties.LINESTYLE__COLOR, LINESTYLE__COLOR_DEFAULT)); - opt.setEscherProperty(new EscherBoolProperty(EscherProperties.FILL__NOFILLHITTEST, 1)); + opt.setEscherProperty(new EscherBoolProperty(EscherProperties.FILL__NOFILLHITTEST, NO_FILLHITTEST_FALSE)); opt.setEscherProperty(new EscherBoolProperty( EscherProperties.GROUPSHAPE__PRINT, 0x080000)); EscherRecord anchor = getAnchor().getEscherAnchor(); @@ -142,34 +129,25 @@ public class HSSFTextbox extends HSSFSimpleShape { } @Override - void afterInsert(HSSFPatriarch patriarch){ - EscherAggregate agg = patriarch._getBoundAggregate(); - agg.associateShapeToObjRecord(_escherContainer.getChildById(EscherClientDataRecord.RECORD_ID), getObjRecord()); - agg.associateShapeToObjRecord(_escherContainer.getChildById(EscherTextboxRecord.RECORD_ID), getTextObjectRecord()); - } - - /** - * @return the rich text string for this textbox. - */ - public HSSFRichTextString getString() { - return _textObjectRecord.getStr(); - } - - /** - * @param string Sets the rich text string used by this object. - */ public void setString(RichTextString string) { HSSFRichTextString rtr = (HSSFRichTextString) string; // If font is not set we must set the default one if (rtr.numFormattingRuns() == 0) rtr.applyFont((short) 0); - _textObjectRecord.setStr(rtr); + getTextObjectRecord().setStr(rtr); + } + + @Override + void afterInsert(HSSFPatriarch patriarch){ + EscherAggregate agg = patriarch._getBoundAggregate(); + agg.associateShapeToObjRecord(getEscherContainer().getChildById(EscherClientDataRecord.RECORD_ID), getObjRecord()); + agg.associateShapeToObjRecord(getEscherContainer().getChildById(EscherTextboxRecord.RECORD_ID), getTextObjectRecord()); } /** * @return Returns the left margin within the textbox. */ public int getMarginLeft() { - EscherSimpleProperty property = _optRecord.lookup(EscherProperties.TEXT__TEXTLEFT); + EscherSimpleProperty property = getOptRecord().lookup(EscherProperties.TEXT__TEXTLEFT); return property == null ? 0: property.getPropertyValue(); } @@ -184,7 +162,7 @@ public class HSSFTextbox extends HSSFSimpleShape { * @return returns the right margin within the textbox. */ public int getMarginRight() { - EscherSimpleProperty property = _optRecord.lookup(EscherProperties.TEXT__TEXTRIGHT); + EscherSimpleProperty property = getOptRecord().lookup(EscherProperties.TEXT__TEXTRIGHT); return property == null ? 0: property.getPropertyValue(); } @@ -199,7 +177,7 @@ public class HSSFTextbox extends HSSFSimpleShape { * @return returns the top margin within the textbox. */ public int getMarginTop() { - EscherSimpleProperty property = _optRecord.lookup(EscherProperties.TEXT__TEXTTOP); + EscherSimpleProperty property = getOptRecord().lookup(EscherProperties.TEXT__TEXTTOP); return property == null ? 0: property.getPropertyValue(); } @@ -214,7 +192,7 @@ public class HSSFTextbox extends HSSFSimpleShape { * Gets the bottom margin within the textbox. */ public int getMarginBottom() { - EscherSimpleProperty property = _optRecord.lookup(EscherProperties.TEXT__TEXTBOTTOM); + EscherSimpleProperty property = getOptRecord().lookup(EscherProperties.TEXT__TEXTBOTTOM); return property == null ? 0: property.getPropertyValue(); } @@ -229,34 +207,32 @@ public class HSSFTextbox extends HSSFSimpleShape { * Gets the horizontal alignment. */ public short getHorizontalAlignment() { - return (short) _textObjectRecord.getHorizontalTextAlignment(); + return (short) getTextObjectRecord().getHorizontalTextAlignment(); } /** * Sets the horizontal alignment. */ public void setHorizontalAlignment(short align) { - _textObjectRecord.setHorizontalTextAlignment(align); + getTextObjectRecord().setHorizontalTextAlignment(align); } /** * Gets the vertical alignment. */ public short getVerticalAlignment() { - return (short) _textObjectRecord.getVerticalTextAlignment(); + return (short) getTextObjectRecord().getVerticalTextAlignment(); } /** * Sets the vertical alignment. */ public void setVerticalAlignment(short align) { - _textObjectRecord.setVerticalTextAlignment(align); - } - - public TextObjectRecord getTextObjectRecord() { - return _textObjectRecord; + getTextObjectRecord().setVerticalTextAlignment(align); } @Override - public void setShapeType(int shapeType) {/**DO NOTHING**/} + public void setShapeType(int shapeType) { + throw new IllegalStateException("Shape type can not be changed in "+this.getClass().getSimpleName()); + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFUnknownShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFUnknownShape.java index 406dfe7219..827a5c34ac 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFUnknownShape.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFUnknownShape.java @@ -33,11 +33,19 @@ public class HSSFUnknownShape extends HSSFShape { @Override protected EscherContainerRecord createSpContainer() { - return null; //To change body of implemented methods use File | Settings | File Templates. + return null; } @Override protected ObjRecord createObjRecord() { - return null; //To change body of implemented methods use File | Settings | File Templates. + return null; + } + + @Override + protected void afterRemove(HSSFPatriarch patriarch) { + } + + @Override + void afterInsert(HSSFPatriarch patriarch) { } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index afd0bce10f..e24ccc2d91 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -1614,7 +1614,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss r.setUid( uid ); r.setTag( (short) 0xFF ); r.setSize( pictureData.length + 25 ); - r.setRef( 1 ); + r.setRef( 0 ); r.setOffset( 0 ); r.setBlipRecord( blipRecord ); diff --git a/src/testcases/org/apache/poi/hssf/model/TestDrawingShapes.java b/src/testcases/org/apache/poi/hssf/model/TestDrawingShapes.java index e50aa47adf..06564e94c4 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestDrawingShapes.java +++ b/src/testcases/org/apache/poi/hssf/model/TestDrawingShapes.java @@ -47,7 +47,8 @@ public class TestDrawingShapes extends TestCase { } public void testHSSFShapeCompatibility() { - HSSFShape shape = new HSSFSimpleShape(null, new HSSFClientAnchor()); + HSSFSimpleShape shape = new HSSFSimpleShape(null, new HSSFClientAnchor()); + shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE); assertEquals(0x08000040, shape.getLineStyleColor()); assertEquals(0x08000009, shape.getFillColor()); assertEquals(HSSFShape.LINEWIDTH_DEFAULT, shape.getLineWidth()); @@ -80,7 +81,7 @@ public class TestDrawingShapes extends TestCase { HSSFPicture picture = new HSSFPicture(null, new HSSFClientAnchor()); assertEquals(picture.getLineWidth(), HSSFShape.LINEWIDTH_DEFAULT); assertEquals(picture.getFillColor(), HSSFShape.FILL__FILLCOLOR_DEFAULT); - assertEquals(picture.getLineStyle(), HSSFShape.LINESTYLE_SOLID); + assertEquals(picture.getLineStyle(), HSSFShape.LINESTYLE_NONE); assertEquals(picture.getLineStyleColor(), HSSFShape.LINESTYLE__COLOR_DEFAULT); assertEquals(picture.isNoFill(), false); assertEquals(picture.getPictureIndex(), -1);//not set yet @@ -131,8 +132,10 @@ public class TestDrawingShapes extends TestCase { assertEquals(10, rectangle.getLineStyle()); rectangle.setLineStyleColor(1111); rectangle.setNoFill(true); + rectangle.setString(new HSSFRichTextString("teeeest")); assertEquals(rectangle.getLineStyleColor(), 1111); assertEquals(rectangle.isNoFill(), true); + assertEquals(rectangle.getString().getString(), "teeeest"); wb = HSSFTestDataSamples.writeOutAndReadBack(wb); sheet = wb.getSheetAt(0); @@ -149,6 +152,7 @@ public class TestDrawingShapes extends TestCase { assertEquals(rectangle2.getLineStyleColor(), 1111); assertEquals(rectangle2.getFillColor(), 777); assertEquals(rectangle2.isNoFill(), true); + assertEquals(rectangle2.getString().getString(), "teeeest"); rectangle2.setFillColor(3333); rectangle2.setLineStyle(9); @@ -159,6 +163,7 @@ public class TestDrawingShapes extends TestCase { rectangle2.getAnchor().setDx2(3); rectangle2.getAnchor().setDy1(4); rectangle2.getAnchor().setDy2(5); + rectangle2.setString(new HSSFRichTextString("test22")); wb = HSSFTestDataSamples.writeOutAndReadBack(wb); sheet = wb.getSheetAt(0); @@ -175,6 +180,7 @@ public class TestDrawingShapes extends TestCase { assertEquals(rectangle2.getAnchor().getDy1(), 4); assertEquals(rectangle2.getAnchor().getDy2(), 5); assertEquals(rectangle2.isNoFill(), false); + assertEquals(rectangle2.getString().getString(), "test22"); HSSFSimpleShape rect3 = drawing.createSimpleShape(new HSSFClientAnchor()); rect3.setShapeType(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE); @@ -196,7 +202,7 @@ public class TestDrawingShapes extends TestCase { assertEquals(picture.getFillColor(), 0x5DC943); assertEquals(picture.getLineWidth(), HSSFShape.LINEWIDTH_DEFAULT); assertEquals(picture.getLineStyle(), HSSFShape.LINESTYLE_DEFAULT); - assertEquals(picture.isNoFill(), true); + assertEquals(picture.isNoFill(), false); picture.setPictureIndex(2); assertEquals(picture.getPictureIndex(), 2); @@ -211,11 +217,12 @@ public class TestDrawingShapes extends TestCase { assertEquals(1, drawing.getChildren().size()); HSSFSimpleShape shape = (HSSFSimpleShape) drawing.getChildren().get(0); - assertEquals(shape.isNoFill(), true); + assertEquals(shape.isNoFill(), false); assertEquals(shape.getLineStyle(), HSSFShape.LINESTYLE_DASHDOTGEL); assertEquals(shape.getLineStyleColor(), 0x616161); assertEquals(HexDump.toHex(shape.getFillColor()), shape.getFillColor(), 0x2CE03D); assertEquals(shape.getLineWidth(), HSSFShape.LINEWIDTH_ONE_PT * 2); + assertEquals(shape.getString().getString(), "POItest"); } public void testShapeIds() { diff --git a/src/testcases/org/apache/poi/hssf/model/TestHSSFAnchor.java b/src/testcases/org/apache/poi/hssf/model/TestHSSFAnchor.java index 248247266a..9134332834 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestHSSFAnchor.java +++ b/src/testcases/org/apache/poi/hssf/model/TestHSSFAnchor.java @@ -375,4 +375,38 @@ public class TestHSSFAnchor extends TestCase { childAnchor2.setDy2(3); assertEquals(childAnchor1, childAnchor2); } + + public void testFlipped(){ + HSSFChildAnchor child = new HSSFChildAnchor(2,2,1,1); + assertEquals(child.isHorizontallyFlipped(), true); + assertEquals(child.isVerticallyFlipped(), true); + assertEquals(child.getDx1(), 1); + assertEquals(child.getDx2(), 2); + assertEquals(child.getDy1(), 1); + assertEquals(child.getDy2(), 2); + + child = new HSSFChildAnchor(3,3,4,4); + assertEquals(child.isHorizontallyFlipped(), false); + assertEquals(child.isVerticallyFlipped(), false); + assertEquals(child.getDx1(), 3); + assertEquals(child.getDx2(), 4); + assertEquals(child.getDy1(), 3); + assertEquals(child.getDy2(), 4); + + HSSFClientAnchor client = new HSSFClientAnchor(1,1,1,1, (short)4,4,(short)3,3); + assertEquals(client.isVerticallyFlipped(), true); + assertEquals(client.isHorizontallyFlipped(), true); + assertEquals(client.getCol1(), 3); + assertEquals(client.getCol2(), 4); + assertEquals(client.getRow1(), 3); + assertEquals(client.getRow2(), 4); + + client = new HSSFClientAnchor(1,1,1,1, (short)5,5,(short)6,6); + assertEquals(client.isVerticallyFlipped(), false); + assertEquals(client.isHorizontallyFlipped(), false); + assertEquals(client.getCol1(), 5); + assertEquals(client.getCol2(), 6); + assertEquals(client.getRow1(), 5); + assertEquals(client.getRow2(), 6); + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/HSSFTestHelper.java b/src/testcases/org/apache/poi/hssf/usermodel/HSSFTestHelper.java index 3aafa04edd..d5606362c8 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/HSSFTestHelper.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/HSSFTestHelper.java @@ -83,7 +83,7 @@ public class HSSFTestHelper { } public static EscherOptRecord getOptRecord(HSSFShape shape){ - return shape._optRecord; + return shape.getOptRecord(); } public static void convertHSSFGroup(HSSFShapeGroup shape, EscherContainerRecord escherParent, Map shapeToObj){ diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestComment.java b/src/testcases/org/apache/poi/hssf/usermodel/TestComment.java index 376edc32b1..b098c19737 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestComment.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestComment.java @@ -212,8 +212,7 @@ public class TestComment extends TestCase { HSSFSimpleShape shape = patriarch.createSimpleShape(new HSSFClientAnchor()); - //6 properties of HSSFShape + 8 of HSSFTextbox - assertEquals(comment._optRecord.getEscherProperties().size(), 14); + assertEquals(comment.getOptRecord().getEscherProperties().size(), 10); } public void testShapeId(){ diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java index 6c08a167a1..234cddccfd 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java @@ -17,8 +17,10 @@ package org.apache.poi.hssf.usermodel; +import org.apache.poi.ddf.EscherBSERecord; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.HSSFITestDataProvider; +import org.apache.poi.hssf.model.InternalSheet; import org.apache.poi.ss.usermodel.BaseTestPicture; import org.apache.poi.ss.usermodel.PictureData; import org.apache.poi.ss.usermodel.Workbook; @@ -149,4 +151,63 @@ public final class TestHSSFPicture extends BaseTestPicture { assertTrue(Arrays.equals(data4, ((HSSFPicture)dr.getChildren().get(3)).getPictureData().getData())); } + public void testBSEPictureRef(){ + HSSFWorkbook wb = new HSSFWorkbook(); + + HSSFSheet sh = wb.createSheet("Pictures"); + HSSFPatriarch dr = sh.createDrawingPatriarch(); + HSSFClientAnchor anchor = new HSSFClientAnchor(); + + InternalSheet ish = HSSFTestHelper.getSheetForTest(sh); + + //register a picture + byte[] data1 = new byte[]{1, 2, 3}; + int idx1 = wb.addPicture(data1, Workbook.PICTURE_TYPE_JPEG); + assertEquals(1, idx1); + HSSFPicture p1 = dr.createPicture(anchor, idx1); + + EscherBSERecord bse = wb.getWorkbook().getBSERecord(idx1); + + assertEquals(bse.getRef(), 1); + dr.createPicture(new HSSFClientAnchor(), idx1); + assertEquals(bse.getRef(), 2); + + HSSFShapeGroup gr = dr.createGroup(new HSSFClientAnchor()); + gr.createPicture(new HSSFChildAnchor(), idx1); + assertEquals(bse.getRef(), 3); + } + + public void testReadExistingImage(){ + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("drawings.xls"); + HSSFSheet sheet = wb.getSheet("picture"); + HSSFPatriarch drawing = sheet.getDrawingPatriarch(); + assertEquals(1, drawing.getChildren().size()); + + HSSFPicture picture = (HSSFPicture) drawing.getChildren().get(0); + assertEquals(picture.getAdditionalData(), "test"); + } + + public void testSetGetProperties(){ + HSSFWorkbook wb = new HSSFWorkbook(); + + HSSFSheet sh = wb.createSheet("Pictures"); + HSSFPatriarch dr = sh.createDrawingPatriarch(); + HSSFClientAnchor anchor = new HSSFClientAnchor(); + + //register a picture + byte[] data1 = new byte[]{1, 2, 3}; + int idx1 = wb.addPicture(data1, Workbook.PICTURE_TYPE_JPEG); + HSSFPicture p1 = dr.createPicture(anchor, idx1); + + assertEquals(p1.getAdditionalData(), ""); + p1.setAdditionalData("aaa"); + assertEquals(p1.getAdditionalData(), "aaa"); + + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + sh = wb.getSheet("Pictures"); + dr = sh.getDrawingPatriarch(); + + p1 = (HSSFPicture) dr.getChildren().get(0); + assertEquals(p1.getAdditionalData(), "aaa"); + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestPolygon.java b/src/testcases/org/apache/poi/hssf/usermodel/TestPolygon.java index 7c83a0d25f..3983c838b2 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestPolygon.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestPolygon.java @@ -73,7 +73,7 @@ public class TestPolygon extends TestCase{ PolygonShape polygonShape = HSSFTestModelHelper.createPolygonShape(0, polygon); - EscherArrayProperty verticesProp1 = polygon._optRecord.lookup(EscherProperties.GEOMETRY__VERTICES); + EscherArrayProperty verticesProp1 = polygon.getOptRecord().lookup(EscherProperties.GEOMETRY__VERTICES); EscherArrayProperty verticesProp2 = ((EscherOptRecord)polygonShape.getSpContainer().getChildById(EscherOptRecord.RECORD_ID)) .lookup(EscherProperties.GEOMETRY__VERTICES); @@ -85,7 +85,7 @@ public class TestPolygon extends TestCase{ assertTrue(Arrays.equals(polygon.getYPoints(), new int[]{4, 5, 6})); polygonShape = HSSFTestModelHelper.createPolygonShape(0, polygon); - verticesProp1 = polygon._optRecord.lookup(EscherProperties.GEOMETRY__VERTICES); + verticesProp1 = polygon.getOptRecord().lookup(EscherProperties.GEOMETRY__VERTICES); verticesProp2 = ((EscherOptRecord)polygonShape.getSpContainer().getChildById(EscherOptRecord.RECORD_ID)) .lookup(EscherProperties.GEOMETRY__VERTICES); diff --git a/test-data/spreadsheet/drawings.xls b/test-data/spreadsheet/drawings.xls index 9700c91086f0b771299ce855063de7b2e1c19bcd..eeac150c3f4bf3c2329e3cecf5c56b706220d337 100644 GIT binary patch delta 18459 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 A2><{9 delta 4326 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 ZPNF_7VBNr