From: Andreas Beeker Date: Fri, 2 Jan 2015 22:57:05 +0000 (+0000) Subject: Test and fix for bug #46441 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0ea9f5fff9f9e5ff286fbb019a2e8c3bf20407e4;p=poi.git Test and fix for bug #46441 Use generics on Shape.getEscherChild() and SimpleShape.getClientDataRecord() to minimize casting Unify access to Shape.getEscherOptRecord() git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1649152 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/ddf/EscherArrayProperty.java b/src/java/org/apache/poi/ddf/EscherArrayProperty.java index 01e2f5c365..880057e0ff 100644 --- a/src/java/org/apache/poi/ddf/EscherArrayProperty.java +++ b/src/java/org/apache/poi/ddf/EscherArrayProperty.java @@ -17,8 +17,11 @@ package org.apache.poi.ddf; -import org.apache.poi.util.LittleEndian; +import java.util.Iterator; +import java.util.NoSuchElementException; + import org.apache.poi.util.HexDump; +import org.apache.poi.util.LittleEndian; /** * Escher array properties are the most wierd construction ever invented @@ -26,7 +29,7 @@ import org.apache.poi.util.HexDump; * * @author Glen Stampoultzis (glens at superlinksoftware.com) */ -public final class EscherArrayProperty extends EscherComplexProperty { +public final class EscherArrayProperty extends EscherComplexProperty implements Iterable { /** * The size of the header that goes at the * start of the array, before the data @@ -205,4 +208,24 @@ public final class EscherArrayProperty extends EscherComplexProperty { } return sizeOfElements; } + + public Iterator iterator() { + return new Iterator(){ + int idx = 0; + public boolean hasNext() { + return (idx < getNumberOfElementsInArray()); + } + + public byte[] next() { + if (!hasNext()) throw new NoSuchElementException(); + return getElement(idx++); + } + + public void remove() { + throw new UnsupportedOperationException("not yet implemented"); + } + }; + } + + } diff --git a/src/java/org/apache/poi/ddf/EscherColorRef.java b/src/java/org/apache/poi/ddf/EscherColorRef.java new file mode 100644 index 0000000000..dd2626b11e --- /dev/null +++ b/src/java/org/apache/poi/ddf/EscherColorRef.java @@ -0,0 +1,283 @@ +/* ==================================================================== + 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.ddf; + +import org.apache.poi.util.BitField; +import org.apache.poi.util.LittleEndian; + +/** + * An OfficeArtCOLORREF structure entry which also handles color extension opid data + */ +public class EscherColorRef { + int opid = -1; + int colorRef = 0; + + public enum SysIndexSource { + /** Use the fill color of the shape. */ + FILL_COLOR(0xF0), + /** If the shape contains a line, use the line color of the shape. Otherwise, use the fill color. */ + LINE_OR_FILL_COLOR(0xF1), + /** Use the line color of the shape. */ + LINE_COLOR(0xF2), + /** Use the shadow color of the shape. */ + SHADOW_COLOR(0xF3), + /** Use the current, or last-used, color. */ + CURRENT_OR_LAST_COLOR(0xF4), + /** Use the fill background color of the shape. */ + FILL_BACKGROUND_COLOR(0xF5), + /** Use the line background color of the shape. */ + LINE_BACKGROUND_COLOR(0xF6), + /** If the shape contains a fill, use the fill color of the shape. Otherwise, use the line color. */ + FILL_OR_LINE_COLOR(0xF7) + ; + int value; + SysIndexSource(int value) { this.value = value; } + } + + /** + * The following enum specifies values that indicate special procedural properties that + * are used to modify the color components of another color. These values are combined with + * those of the {@link SysIndexSource} enum or with a user-specified color. + * The first six values are mutually exclusive. + */ + public enum SysIndexProcedure { + /** + * Darken the color by the value that is specified in the blue field. + * A blue value of 0xFF specifies that the color is to be left unchanged, + * whereas a blue value of 0x00 specifies that the color is to be completely darkened. + */ + DARKEN_COLOR(0x01), + /** + * Lighten the color by the value that is specified in the blue field. + * A blue value of 0xFF specifies that the color is to be left unchanged, + * whereas a blue value of 0x00 specifies that the color is to be completely lightened. + */ + LIGHTEN_COLOR(0x02), + /** + * Add a gray level RGB value. The blue field contains the gray level to add: + * NewColor = SourceColor + gray + */ + ADD_GRAY_LEVEL(0x03), + /** + * Subtract a gray level RGB value. The blue field contains the gray level to subtract: + * NewColor = SourceColor - gray + */ + SUB_GRAY_LEVEL(0x04), + /** + * Reverse-subtract a gray level RGB value. The blue field contains the gray level from + * which to subtract: + * NewColor = gray - SourceColor + */ + REVERSE_GRAY_LEVEL(0x05), + /** + * If the color component being modified is less than the parameter contained in the blue + * field, set it to the minimum intensity. If the color component being modified is greater + * than or equal to the parameter, set it to the maximum intensity. + */ + THRESHOLD(0x06), + /** + * After making other modifications, invert the color. + * This enum value is only for documentation and won't be directly returned. + */ + INVERT_AFTER(0x20), + /** + * After making other modifications, invert the color by toggling just the high bit of each + * color channel. + * This enum value is only for documentation and won't be directly returned. + */ + INVERT_HIGHBIT_AFTER(0x40) + ; + BitField mask; + SysIndexProcedure(int mask) { + this.mask = new BitField(mask); + } + } + + /** + * A bit that specifies whether the system color scheme will be used to determine the color. + * A value of 0x1 specifies that green and red will be treated as an unsigned 16-bit index + * into the system color table. Values less than 0x00F0 map directly to system colors. + */ + private static final BitField FLAG_SYS_INDEX = new BitField(0x10000000); + + /** + * A bit that specifies whether the current application-defined color scheme will be used + * to determine the color. A value of 0x1 specifies that red will be treated as an index + * into the current color scheme table. If this value is 0x1, green and blue MUST be 0x00. + */ + private static final BitField FLAG_SCHEME_INDEX = new BitField(0x08000000); + + /** + * A bit that specifies whether the color is a standard RGB color. + * 0x0 : The RGB color MAY use halftone dithering to display. + * 0x1 : The color MUST be a solid color. + */ + private static final BitField FLAG_SYSTEM_RGB = new BitField(0x04000000); + + /** + * A bit that specifies whether the current palette will be used to determine the color. + * A value of 0x1 specifies that red, green, and blue contain an RGB value that will be + * matched in the current color palette. This color MUST be solid. + */ + private static final BitField FLAG_PALETTE_RGB = new BitField(0x02000000); + + /** + * A bit that specifies whether the current palette will be used to determine the color. + * A value of 0x1 specifies that green and red will be treated as an unsigned 16-bit index into + * the current color palette. This color MAY be dithered. If this value is 0x1, blue MUST be 0x00. + */ + private static final BitField FLAG_PALETTE_INDEX = new BitField(0x01000000); + + /** + * An unsigned integer that specifies the intensity of the blue color channel. A value + * of 0x00 has the minimum blue intensity. A value of 0xFF has the maximum blue intensity. + */ + private static final BitField FLAG_BLUE = new BitField(0x00FF0000); + + /** + * An unsigned integer that specifies the intensity of the green color channel. A value + * of 0x00 has the minimum green intensity. A value of 0xFF has the maximum green intensity. + */ + private static final BitField FLAG_GREEN = new BitField(0x0000FF00); + + /** + * An unsigned integer that specifies the intensity of the red color channel. A value + * of 0x00 has the minimum red intensity. A value of 0xFF has the maximum red intensity. + */ + private static final BitField FLAG_RED = new BitField(0x000000FF); + + public EscherColorRef(int colorRef) { + this.colorRef = colorRef; + } + + public EscherColorRef(byte[] source, int start, int len) { + assert(len == 4 || len == 6); + + int offset = start; + if (len == 6) { + opid = LittleEndian.getUShort(source, offset); + offset += 2; + } + colorRef = LittleEndian.getInt(source, offset); + } + + public boolean hasSysIndexFlag() { + return FLAG_SYS_INDEX.isSet(colorRef); + } + + public void setSysIndexFlag(boolean flag) { + FLAG_SYS_INDEX.setBoolean(colorRef, flag); + } + + public boolean hasSchemeIndexFlag() { + return FLAG_SCHEME_INDEX.isSet(colorRef); + } + + public void setSchemeIndexFlag(boolean flag) { + FLAG_SCHEME_INDEX.setBoolean(colorRef, flag); + } + + public boolean hasSystemRGBFlag() { + return FLAG_SYSTEM_RGB.isSet(colorRef); + } + + public void setSystemRGBFlag(boolean flag) { + FLAG_SYSTEM_RGB.setBoolean(colorRef, flag); + } + + public boolean hasPaletteRGBFlag() { + return FLAG_PALETTE_RGB.isSet(colorRef); + } + + public void setPaletteRGBFlag(boolean flag) { + FLAG_PALETTE_RGB.setBoolean(colorRef, flag); + } + + public boolean hasPaletteIndexFlag() { + return FLAG_PALETTE_INDEX.isSet(colorRef); + } + + public void setPaletteIndexFlag(boolean flag) { + FLAG_PALETTE_INDEX.setBoolean(colorRef, flag); + } + + public int[] getRGB() { + int rgb[] = { + FLAG_RED.getValue(colorRef), + FLAG_GREEN.getValue(colorRef), + FLAG_BLUE.getValue(colorRef) + }; + return rgb; + } + + /** + * @return {@link SysIndexSource} if {@link #hasSysIndexFlag()} is {@code true}, otherwise null + */ + public SysIndexSource getSysIndexSource() { + if (!hasSysIndexFlag()) return null; + int val = FLAG_RED.getValue(colorRef); + for (SysIndexSource sis : SysIndexSource.values()) { + if (sis.value == val) return sis; + } + return null; + } + + /** + * Return the {@link SysIndexProcedure} - for invert flag use {@link #getSysIndexInvert()} + * @return {@link SysIndexProcedure} if {@link #hasSysIndexFlag()} is {@code true}, otherwise null + */ + public SysIndexProcedure getSysIndexProcedure() { + if (!hasSysIndexFlag()) return null; + int val = FLAG_RED.getValue(colorRef); + for (SysIndexProcedure sip : SysIndexProcedure.values()) { + if (sip == SysIndexProcedure.INVERT_AFTER || sip == SysIndexProcedure.INVERT_HIGHBIT_AFTER) continue; + if (sip.mask.isSet(val)) return sip; + } + return null; + } + + /** + * @return 0 for no invert flag, 1 for {@link SysIndexProcedure#INVERT_AFTER} and + * 2 for {@link SysIndexProcedure#INVERT_HIGHBIT_AFTER} + */ + public int getSysIndexInvert() { + if (!hasSysIndexFlag()) return 0; + int val = FLAG_GREEN.getValue(colorRef); + if ((SysIndexProcedure.INVERT_AFTER.mask.isSet(val))) return 1; + if ((SysIndexProcedure.INVERT_HIGHBIT_AFTER.mask.isSet(val))) return 2; + return 0; + } + + /** + * @return index of the scheme color or -1 if {@link #hasSchemeIndexFlag()} is {@code false} + * + * @see org.apache.poi.hslf.record.ColorSchemeAtom#getColor(int) + */ + public int getSchemeIndex() { + if (!hasSchemeIndexFlag()) return -1; + return FLAG_RED.getValue(colorRef); + } + + /** + * @return index of current palette (color) or -1 if {@link #hasPaletteIndexFlag()} is {@code false} + */ + public int getPaletteIndex() { + if (!hasPaletteIndexFlag()) return -1; + return (FLAG_GREEN.getValue(colorRef) << 8) & FLAG_RED.getValue(colorRef); + } +} diff --git a/src/java/org/apache/poi/util/Units.java b/src/java/org/apache/poi/util/Units.java index 91584b2ad5..d402156abd 100644 --- a/src/java/org/apache/poi/util/Units.java +++ b/src/java/org/apache/poi/util/Units.java @@ -40,4 +40,19 @@ public class Units { public static double toPoints(long emu){ return (double)emu/EMU_PER_POINT; } + + /** + * Converts a value of type FixedPoint to a decimal number + * + * @param fixedPoint + * @return decimal number + * + * @see [MS-OSHARED] - 2.2.1.6 FixedPoint + */ + public static double fixedPointToDecimal(int fixedPoint) { + int i = (fixedPoint >> 16); + int f = (fixedPoint >> 0) & 0xFFFF; + double decimal = (i + f/65536.0); + return decimal; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java index 13ec66bb6f..525f673c3e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java @@ -123,7 +123,7 @@ public final class ActiveXShape extends Picture { public int getControlIndex(){ int idx = -1; - OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID); + OEShapeAtom oe = getClientDataRecord(RecordTypes.OEShapeAtom.typeID); if(oe != null) idx = oe.getOptions(); return idx; } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Fill.java b/src/scratchpad/src/org/apache/poi/hslf/model/Fill.java index 036219a258..5302e267d0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Fill.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Fill.java @@ -17,15 +17,20 @@ package org.apache.poi.hslf.model; -import org.apache.poi.ddf.*; -import org.apache.poi.hslf.record.*; +import java.awt.Color; +import java.util.List; + +import org.apache.poi.ddf.EscherBSERecord; +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.EscherOptRecord; +import org.apache.poi.ddf.EscherProperties; +import org.apache.poi.ddf.EscherRecord; +import org.apache.poi.ddf.EscherSimpleProperty; +import org.apache.poi.hslf.record.Document; import org.apache.poi.hslf.usermodel.PictureData; import org.apache.poi.hslf.usermodel.SlideShow; -import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogFactory; -import java.util.List; - -import java.awt.*; +import org.apache.poi.util.POILogger; /** * Represents functionality provided by the 'Fill Effects' dialog in PowerPoint. @@ -112,16 +117,16 @@ public final class Fill { * @return type of fill */ public int getFillType(){ - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID); - EscherSimpleProperty prop = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__FILLTYPE); + EscherOptRecord opt = shape.getEscherOptRecord(); + EscherSimpleProperty prop = Shape.getEscherProperty(opt, EscherProperties.FILL__FILLTYPE); return prop == null ? FILL_SOLID : prop.getPropertyValue(); } /** */ protected void afterInsert(Sheet sh){ - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID); - EscherSimpleProperty p = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE); + EscherOptRecord opt = shape.getEscherOptRecord(); + EscherSimpleProperty p = Shape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE); if(p != null) { int idx = p.getPropertyValue(); EscherBSERecord bse = getEscherBSERecord(idx); @@ -138,12 +143,12 @@ public final class Fill { SlideShow ppt = sheet.getSlideShow(); Document doc = ppt.getDocumentRecord(); EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer(); - EscherContainerRecord bstore = (EscherContainerRecord)Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER); + EscherContainerRecord bstore = Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER); if(bstore == null) { logger.log(POILogger.DEBUG, "EscherContainerRecord.BSTORE_CONTAINER was not found "); return null; } - List lst = bstore.getChildRecords(); + List lst = bstore.getChildRecords(); return (EscherBSERecord)lst.get(idx-1); } @@ -154,7 +159,7 @@ public final class Fill { * @param type type of the fill */ public void setFillType(int type){ - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID); + EscherOptRecord opt = shape.getEscherOptRecord(); Shape.setEscherProperty(opt, EscherProperties.FILL__FILLTYPE, type); } @@ -162,8 +167,8 @@ public final class Fill { * Foreground color */ public Color getForegroundColor(){ - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID); - EscherSimpleProperty p = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); + EscherOptRecord opt = shape.getEscherOptRecord(); + EscherSimpleProperty p = Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); if(p != null && (p.getPropertyValue() & 0x10) == 0) return null; @@ -175,7 +180,7 @@ public final class Fill { * Foreground color */ public void setForegroundColor(Color color){ - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID); + EscherOptRecord opt = shape.getEscherOptRecord(); if (color == null) { Shape.setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150000); } @@ -190,8 +195,8 @@ public final class Fill { * Background color */ public Color getBackgroundColor(){ - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID); - EscherSimpleProperty p = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); + EscherOptRecord opt = shape.getEscherOptRecord(); + EscherSimpleProperty p = Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); if(p != null && (p.getPropertyValue() & 0x10) == 0) return null; @@ -202,7 +207,7 @@ public final class Fill { * Background color */ public void setBackgroundColor(Color color){ - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID); + EscherOptRecord opt = shape.getEscherOptRecord(); if (color == null) { Shape.setEscherProperty(opt, EscherProperties.FILL__FILLBACKCOLOR, -1); } @@ -216,8 +221,8 @@ public final class Fill { * PictureData object used in a texture, pattern of picture fill. */ public PictureData getPictureData(){ - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID); - EscherSimpleProperty p = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE); + EscherOptRecord opt = shape.getEscherOptRecord(); + EscherSimpleProperty p = Shape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE); if (p == null) return null; SlideShow ppt = shape.getSheet().getSlideShow(); @@ -225,7 +230,7 @@ public final class Fill { Document doc = ppt.getDocumentRecord(); EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer(); - EscherContainerRecord bstore = (EscherContainerRecord)Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER); + EscherContainerRecord bstore = Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER); java.util.List lst = bstore.getChildRecords(); int idx = p.getPropertyValue(); @@ -249,7 +254,7 @@ public final class Fill { * @param idx 0-based index of the picture added to this ppt by SlideShow.addPicture method. */ public void setPictureData(int idx){ - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID); + EscherOptRecord opt = shape.getEscherOptRecord(); Shape.setEscherProperty(opt, (short)(EscherProperties.FILL__PATTERNTEXTURE + 0x4000), idx); if( idx != 0 ) { if( shape.getSheet() != null ) { diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java b/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java index ca647f32d0..82f46b5e1c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java @@ -179,11 +179,11 @@ public final class Freeform extends AutoShape { EscherOptRecord opt = getEscherOptRecord(); opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 0x4)); - EscherArrayProperty verticesProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES + 0x4000)); - if(verticesProp == null) verticesProp = (EscherArrayProperty)getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES); + EscherArrayProperty verticesProp = getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES + 0x4000)); + if(verticesProp == null) verticesProp = getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES); - EscherArrayProperty segmentsProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO + 0x4000)); - if(segmentsProp == null) segmentsProp = (EscherArrayProperty)getEscherProperty(opt, EscherProperties.GEOMETRY__SEGMENTINFO); + EscherArrayProperty segmentsProp = getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO + 0x4000)); + if(segmentsProp == null) segmentsProp = getEscherProperty(opt, EscherProperties.GEOMETRY__SEGMENTINFO); //sanity check if(verticesProp == null) { diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java index 9d066ac944..93e8a124fc 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java @@ -116,10 +116,10 @@ public final class MovieShape extends Picture { * @param idx the index of the movie */ public void setMovieIndex(int idx){ - OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID); + OEShapeAtom oe = getClientDataRecord(RecordTypes.OEShapeAtom.typeID); oe.setOptions(idx); - AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID); + AnimationInfo an = getClientDataRecord(RecordTypes.AnimationInfo.typeID); if(an != null) { AnimationInfoAtom ai = an.getAnimationInfoAtom(); ai.setDimColor(0x07000000); @@ -131,7 +131,7 @@ public final class MovieShape extends Picture { } public void setAutoPlay(boolean flag){ - AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID); + AnimationInfo an = getClientDataRecord(RecordTypes.AnimationInfo.typeID); if(an != null){ an.getAnimationInfoAtom().setFlag(AnimationInfoAtom.Automatic, flag); updateClientData(); @@ -139,7 +139,7 @@ public final class MovieShape extends Picture { } public boolean isAutoPlay(){ - AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID); + AnimationInfo an = getClientDataRecord(RecordTypes.AnimationInfo.typeID); if(an != null){ return an.getAnimationInfoAtom().getFlag(AnimationInfoAtom.Automatic); } @@ -150,7 +150,7 @@ public final class MovieShape extends Picture { * @return UNC or local path to a video file */ public String getPath(){ - OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID); + OEShapeAtom oe = getClientDataRecord(RecordTypes.OEShapeAtom.typeID); int idx = oe.getOptions(); SlideShow ppt = getSheet().getSlideShow(); diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java index ea6777deec..eda9d25056 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java @@ -41,6 +41,7 @@ import org.apache.poi.hslf.usermodel.PictureData; import org.apache.poi.hslf.usermodel.SlideShow; import org.apache.poi.util.POILogger; import org.apache.poi.util.StringUtil; +import org.apache.poi.util.Units; /** @@ -120,7 +121,7 @@ public class Picture extends SimpleShape { */ public int getPictureIndex(){ EscherOptRecord opt = getEscherOptRecord(); - EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.BLIP__BLIPTODISPLAY); + EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.BLIP__BLIPTODISPLAY); return prop == null ? 0 : prop.getPropertyValue(); } @@ -202,7 +203,7 @@ public class Picture extends SimpleShape { SlideShow ppt = getSheet().getSlideShow(); Document doc = ppt.getDocumentRecord(); EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer(); - EscherContainerRecord bstore = (EscherContainerRecord)Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER); + EscherContainerRecord bstore = Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER); if(bstore == null) { logger.log(POILogger.DEBUG, "EscherContainerRecord.BSTORE_CONTAINER was not found "); return null; @@ -223,7 +224,7 @@ public class Picture extends SimpleShape { */ public String getPictureName(){ EscherOptRecord opt = getEscherOptRecord(); - EscherComplexProperty prop = (EscherComplexProperty)getEscherProperty(opt, EscherProperties.BLIP__BLIPFILENAME); + EscherComplexProperty prop = getEscherProperty(opt, EscherProperties.BLIP__BLIPFILENAME); if (prop == null) return null; String name = StringUtil.getFromUnicodeLE(prop.getComplexData()); return name.trim(); @@ -289,16 +290,11 @@ public class Picture extends SimpleShape { /** * @return the fractional property or 0 if not defined - * - * @see 2.2.1.6 FixedPoint */ private static double getFractProp(EscherOptRecord opt, short propertyId) { - EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propertyId); + EscherSimpleProperty prop = getEscherProperty(opt, propertyId); if (prop == null) return 0; int fixedPoint = prop.getPropertyValue(); - int i = (fixedPoint >> 16); - int f = (fixedPoint >> 0) & 0xFFFF; - double fp = i + f/65536.0; - return fp; + return Units.fixedPointToDecimal(fixedPoint); } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java index 1ae07e2b8d..4a136611ca 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java @@ -21,6 +21,7 @@ import org.apache.poi.ddf.*; import org.apache.poi.hslf.record.ColorSchemeAtom; import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.Units; import java.util.*; import java.awt.*; @@ -129,7 +130,7 @@ public abstract class Shape { * @see org.apache.poi.hslf.record.RecordTypes */ public int getShapeType(){ - EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); return spRecord.getShapeType(); } @@ -138,7 +139,7 @@ public abstract class Shape { * @see org.apache.poi.hslf.record.RecordTypes */ public void setShapeType(int type){ - EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); spRecord.setShapeType( (short) type ); spRecord.setVersion( (short) 0x2 ); } @@ -161,15 +162,15 @@ public abstract class Shape { * @return the anchor of this shape */ public Rectangle2D getAnchor2D(){ - EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); int flags = spRecord.getFlags(); Rectangle2D anchor=null; if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ - EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); + EscherChildAnchorRecord rec = getEscherChild(EscherChildAnchorRecord.RECORD_ID); anchor = new java.awt.Rectangle(); if(rec == null){ logger.log(POILogger.WARN, "EscherSpRecord.FLAG_CHILD is set but EscherChildAnchorRecord was not found"); - EscherClientAnchorRecord clrec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); + EscherClientAnchorRecord clrec = getEscherChild(EscherClientAnchorRecord.RECORD_ID); anchor = new java.awt.Rectangle(); anchor = new Rectangle2D.Float( (float)clrec.getCol1()*POINT_DPI/MASTER_DPI, @@ -187,7 +188,7 @@ public abstract class Shape { } } else { - EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); + EscherClientAnchorRecord rec = getEscherChild(EscherClientAnchorRecord.RECORD_ID); anchor = new java.awt.Rectangle(); anchor = new Rectangle2D.Float( (float)rec.getCol1()*POINT_DPI/MASTER_DPI, @@ -210,17 +211,17 @@ public abstract class Shape { * @param anchor new anchor */ public void setAnchor(Rectangle2D anchor){ - EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); int flags = spRecord.getFlags(); if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ - EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); + EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(EscherChildAnchorRecord.RECORD_ID); rec.setDx1((int)(anchor.getX()*MASTER_DPI/POINT_DPI)); rec.setDy1((int)(anchor.getY()*MASTER_DPI/POINT_DPI)); rec.setDx2((int)((anchor.getWidth() + anchor.getX())*MASTER_DPI/POINT_DPI)); rec.setDy2((int)((anchor.getHeight() + anchor.getY())*MASTER_DPI/POINT_DPI)); } else { - EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); + EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(EscherClientAnchorRecord.RECORD_ID); rec.setFlag((short)(anchor.getY()*MASTER_DPI/POINT_DPI)); rec.setCol1((short)(anchor.getX()*MASTER_DPI/POINT_DPI)); rec.setDx1((short)(((anchor.getWidth() + anchor.getX())*MASTER_DPI/POINT_DPI))); @@ -246,29 +247,21 @@ public abstract class Shape { * * @return escher record or null if not found. */ - public static EscherRecord getEscherChild(EscherContainerRecord owner, int recordId){ - for ( Iterator iterator = owner.getChildIterator(); iterator.hasNext(); ) - { - EscherRecord escherRecord = iterator.next(); - if (escherRecord.getRecordId() == recordId) - return escherRecord; - } - return null; + public static T getEscherChild(EscherContainerRecord owner, int recordId){ + return owner.getChildById((short)recordId); } + public T getEscherChild(int recordId){ + return _escherContainer.getChildById((short)recordId); + } + /** * Returns escher property by id. * * @return escher property or null if not found. */ - public static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){ - if(opt != null) for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); ) - { - EscherProperty prop = (EscherProperty) iterator.next(); - if (prop.getPropertyNumber() == propId) - return prop; - } - return null; + public static T getEscherProperty(EscherOptRecord opt, int propId){ + return opt.lookup(propId); } /** @@ -279,11 +272,11 @@ public abstract class Shape { * @param value value of the property. If value = -1 then the property is removed. */ public static void setEscherProperty(EscherOptRecord opt, short propId, int value){ - java.util.List props = opt.getEscherProperties(); - for ( Iterator iterator = props.iterator(); iterator.hasNext(); ) { - EscherProperty prop = (EscherProperty) iterator.next(); - if (prop.getId() == propId){ + java.util.List props = opt.getEscherProperties(); + for ( Iterator iterator = props.iterator(); iterator.hasNext(); ) { + if (iterator.next().getPropertyNumber() == propId){ iterator.remove(); + break; } } if (value != -1) { @@ -310,7 +303,7 @@ public abstract class Shape { */ public int getEscherProperty(short propId){ EscherOptRecord opt = getEscherOptRecord(); - EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propId); + EscherSimpleProperty prop = getEscherProperty(opt, propId); return prop == null ? 0 : prop.getPropertyValue(); } @@ -321,7 +314,7 @@ public abstract class Shape { */ public int getEscherProperty(short propId, int defaultValue){ EscherOptRecord opt = getEscherOptRecord(); - EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propId); + EscherSimpleProperty prop = getEscherProperty(opt, propId); return prop == null ? defaultValue : prop.getPropertyValue(); } @@ -365,32 +358,30 @@ public abstract class Shape { Color getColor(short colorProperty, short opacityProperty, int defaultColor){ EscherOptRecord opt = getEscherOptRecord(); - EscherSimpleProperty p = (EscherSimpleProperty)getEscherProperty(opt, colorProperty); + EscherSimpleProperty p = getEscherProperty(opt, colorProperty); if(p == null && defaultColor == -1) return null; - int val = p == null ? defaultColor : p.getPropertyValue(); - - int a = (val >> 24) & 0xFF; - int b = (val >> 16) & 0xFF; - int g = (val >> 8) & 0xFF; - int r = (val >> 0) & 0xFF; + int val = (p == null) ? defaultColor : p.getPropertyValue(); - boolean fPaletteIndex = (a & 1) != 0; - boolean fPaletteRGB = (a & (1 << 1)) != 0; - boolean fSystemRGB = (a & (1 << 2)) != 0; - boolean fSchemeIndex = (a & (1 << 3)) != 0; - boolean fSysIndex = (a & (1 << 4)) != 0; + EscherColorRef ecr = new EscherColorRef(val); + + boolean fPaletteIndex = ecr.hasPaletteIndexFlag(); + boolean fPaletteRGB = ecr.hasPaletteRGBFlag(); + boolean fSystemRGB = ecr.hasSystemRGBFlag(); + boolean fSchemeIndex = ecr.hasSchemeIndexFlag(); + boolean fSysIndex = ecr.hasSysIndexFlag(); + + int rgb[] = ecr.getRGB(); Sheet sheet = getSheet(); - if (fSchemeIndex && sheet != null) - { + if (fSchemeIndex && sheet != null) { //red is the index to the color scheme ColorSchemeAtom ca = sheet.getColorScheme(); - int schemeColor = ca.getColor(r); + int schemeColor = ca.getColor(ecr.getSchemeIndex()); - r = (schemeColor >> 0) & 0xFF; - g = (schemeColor >> 8) & 0xFF; - b = (schemeColor >> 16) & 0xFF; + rgb[0] = (schemeColor >> 0) & 0xFF; + rgb[1] = (schemeColor >> 8) & 0xFF; + rgb[2] = (schemeColor >> 16) & 0xFF; } else if (fPaletteIndex){ //TODO } else if (fPaletteRGB){ @@ -401,13 +392,11 @@ public abstract class Shape { //TODO } - EscherSimpleProperty op = (EscherSimpleProperty)getEscherProperty(opt, opacityProperty); + EscherSimpleProperty op = getEscherProperty(opt, opacityProperty); int defaultOpacity = 0x00010000; - int opacity = op == null ? defaultOpacity : op.getPropertyValue(); - int i = (opacity >> 16); - int f = (opacity >> 0) & 0xFFFF ; - double alpha = (i + f/65536.0)*255; - return new Color(r, g, b, (int)alpha); + int opacity = (op == null) ? defaultOpacity : op.getPropertyValue(); + double alpha = Units.fixedPointToDecimal(opacity)*255.0; + return new Color(rgb[0], rgb[1], rgb[2], (int)alpha); } Color toRGB(int val){ @@ -436,7 +425,7 @@ public abstract class Shape { * @return id for the shape. */ public int getShapeId(){ - EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); return spRecord == null ? 0 : spRecord.getShapeId(); } @@ -446,7 +435,7 @@ public abstract class Shape { * @param id of the shape */ public void setShapeId(int id){ - EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); if(spRecord != null) spRecord.setShapeId(id); } @@ -484,7 +473,48 @@ public abstract class Shape { return getLogicalAnchor2D(); } - protected EscherOptRecord getEscherOptRecord() { - return (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + public EscherOptRecord getEscherOptRecord() { + return getEscherChild(EscherOptRecord.RECORD_ID); + } + + /** + * Whether the shape is horizontally flipped + * + * @return whether the shape is horizontally flipped + */ + public boolean getFlipHorizontal(){ + EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); + return (spRecord.getFlags()& EscherSpRecord.FLAG_FLIPHORIZ) != 0; + } + + /** + * Whether the shape is vertically flipped + * + * @return whether the shape is vertically flipped + */ + public boolean getFlipVertical(){ + EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); + return (spRecord.getFlags()& EscherSpRecord.FLAG_FLIPVERT) != 0; + } + + /** + * Rotation angle in degrees + * + * @return rotation angle in degrees + */ + public int getRotation(){ + int rot = getEscherProperty(EscherProperties.TRANSFORM__ROTATION); + int angle = (rot >> 16) % 360; + + return angle; + } + + /** + * Rotate this shape + * + * @param theta the rotation angle in degrees + */ + public void setRotation(int theta){ + setEscherProperty(EscherProperties.TRANSFORM__ROTATION, (theta << 16)); } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java index 215835d5be..c3843c2911 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java @@ -62,7 +62,7 @@ public final class ShapeFactory { if(opt != null){ try { EscherPropertyFactory f = new EscherPropertyFactory(); - List props = f.createProperties( opt.serialize(), 8, opt.getInstance() ); + List props = f.createProperties( opt.serialize(), 8, opt.getInstance() ); EscherSimpleProperty p = (EscherSimpleProperty)props.get(0); if(p.getPropertyNumber() == 0x39F && p.getPropertyValue() == 1){ group = new Table(spContainer, parent); @@ -91,8 +91,8 @@ public final class ShapeFactory { break; case ShapeTypes.HostControl: case ShapeTypes.PictureFrame: { - InteractiveInfo info = (InteractiveInfo)getClientDataRecord(spContainer, RecordTypes.InteractiveInfo.typeID); - OEShapeAtom oes = (OEShapeAtom)getClientDataRecord(spContainer, RecordTypes.OEShapeAtom.typeID); + InteractiveInfo info = getClientDataRecord(spContainer, RecordTypes.InteractiveInfo.typeID); + OEShapeAtom oes = getClientDataRecord(spContainer, RecordTypes.OEShapeAtom.typeID); if(info != null && info.getInteractiveInfoAtom() != null){ switch(info.getInteractiveInfoAtom().getAction()){ case InteractiveInfoAtom.ACTION_OLE: @@ -115,7 +115,7 @@ public final class ShapeFactory { shape = new Line(spContainer, parent); break; case ShapeTypes.NotPrimitive: { - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID); + EscherOptRecord opt = Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID); EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES); if(prop != null) shape = new Freeform(spContainer, parent); @@ -134,7 +134,8 @@ public final class ShapeFactory { } - protected static Record getClientDataRecord(EscherContainerRecord spContainer, int recordType) { + @SuppressWarnings("unchecked") + protected static T getClientDataRecord(EscherContainerRecord spContainer, int recordType) { Record oep = null; for (Iterator it = spContainer.getChildIterator(); it.hasNext();) { EscherRecord obj = it.next(); @@ -143,12 +144,12 @@ public final class ShapeFactory { Record[] records = Record.findChildRecords(data, 8, data.length - 8); for (int j = 0; j < records.length; j++) { if (records[j].getRecordType() == recordType) { - return records[j]; + return (T)records[j]; } } } } - return oep; + return (T)oep; } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java index fe9b421687..796b248ac5 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java @@ -100,9 +100,7 @@ public class ShapeGroup extends Shape{ */ public void setAnchor(java.awt.Rectangle anchor){ - EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChild(0); - - EscherClientAnchorRecord clientAnchor = (EscherClientAnchorRecord)getEscherChild(spContainer, EscherClientAnchorRecord.RECORD_ID); + EscherClientAnchorRecord clientAnchor = getEscherChild(EscherClientAnchorRecord.RECORD_ID); //hack. internal variable EscherClientAnchorRecord.shortRecord can be //initialized only in fillFields(). We need to set shortRecord=false; byte[] header = new byte[16]; @@ -116,7 +114,7 @@ public class ShapeGroup extends Shape{ clientAnchor.setDx1((short)((anchor.width + anchor.x)*MASTER_DPI/POINT_DPI)); clientAnchor.setRow1((short)((anchor.height + anchor.y)*MASTER_DPI/POINT_DPI)); - EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(spContainer, EscherSpgrRecord.RECORD_ID); + EscherSpgrRecord spgr = getEscherChild(EscherSpgrRecord.RECORD_ID); spgr.setRectX1(anchor.x*MASTER_DPI/POINT_DPI); spgr.setRectY1(anchor.y*MASTER_DPI/POINT_DPI); @@ -131,8 +129,7 @@ public class ShapeGroup extends Shape{ * @param anchor the coordinate space of this group */ public void setCoordinates(Rectangle2D anchor){ - EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChild(0); - EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(spContainer, EscherSpgrRecord.RECORD_ID); + EscherSpgrRecord spgr = getEscherChild(EscherSpgrRecord.RECORD_ID); int x1 = (int)Math.round(anchor.getX()*MASTER_DPI/POINT_DPI); int y1 = (int)Math.round(anchor.getY()*MASTER_DPI/POINT_DPI); @@ -153,8 +150,7 @@ public class ShapeGroup extends Shape{ * @return the coordinate space of this group */ public Rectangle2D getCoordinates(){ - EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChild(0); - EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(spContainer, EscherSpgrRecord.RECORD_ID); + EscherSpgrRecord spgr = getEscherChild(EscherSpgrRecord.RECORD_ID); Rectangle2D.Float anchor = new Rectangle2D.Float(); anchor.x = (float)spgr.getRectX1()*POINT_DPI/MASTER_DPI; @@ -237,12 +233,11 @@ public class ShapeGroup extends Shape{ * @return the anchor of this shape group */ public Rectangle2D getAnchor2D(){ - EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChild(0); - EscherClientAnchorRecord clientAnchor = (EscherClientAnchorRecord)getEscherChild(spContainer, EscherClientAnchorRecord.RECORD_ID); + EscherClientAnchorRecord clientAnchor = getEscherChild(EscherClientAnchorRecord.RECORD_ID); Rectangle2D.Float anchor = new Rectangle2D.Float(); if(clientAnchor == null){ logger.log(POILogger.INFO, "EscherClientAnchorRecord was not found for shape group. Searching for EscherChildAnchorRecord."); - EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(spContainer, EscherChildAnchorRecord.RECORD_ID); + EscherChildAnchorRecord rec = getEscherChild(EscherChildAnchorRecord.RECORD_ID); anchor = new Rectangle2D.Float( (float)rec.getDx1()*POINT_DPI/MASTER_DPI, (float)rec.getDy1()*POINT_DPI/MASTER_DPI, @@ -266,8 +261,7 @@ public class ShapeGroup extends Shape{ * @return type of the shape. */ public int getShapeType(){ - EscherContainerRecord groupInfoContainer = (EscherContainerRecord)_escherContainer.getChild(0); - EscherSpRecord spRecord = groupInfoContainer.getChildById(EscherSpRecord.RECORD_ID); + EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); return spRecord.getOptions() >> 4; } @@ -291,4 +285,10 @@ public class ShapeGroup extends Shape{ graphics.setTransform(at); } + + @Override + public T getEscherChild(int recordId){ + EscherContainerRecord groupInfoContainer = (EscherContainerRecord)_escherContainer.getChild(0); + return groupInfoContainer.getChildById((short)recordId); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java index e912edf528..c5256b1d2a 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java @@ -484,7 +484,7 @@ public abstract class Sheet { placeholderId = oep.getPlaceholderId(); } else { //special case for files saved in Office 2007 - RoundTripHFPlaceholder12 hldr = (RoundTripHFPlaceholder12)tx.getClientDataRecord(RecordTypes.RoundTripHFPlaceholder12.typeID); + RoundTripHFPlaceholder12 hldr = tx.getClientDataRecord(RecordTypes.RoundTripHFPlaceholder12.typeID); if(hldr != null) placeholderId = hldr.getPlaceholderId(); } if(placeholderId == type){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java index 4a8bdf692e..34db620951 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java @@ -23,9 +23,16 @@ import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.io.ByteArrayOutputStream; import java.util.ArrayList; -import java.util.List; -import org.apache.poi.ddf.*; +import org.apache.poi.ddf.DefaultEscherRecordFactory; +import org.apache.poi.ddf.EscherChildAnchorRecord; +import org.apache.poi.ddf.EscherClientAnchorRecord; +import org.apache.poi.ddf.EscherClientDataRecord; +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.EscherOptRecord; +import org.apache.poi.ddf.EscherProperties; +import org.apache.poi.ddf.EscherRecord; +import org.apache.poi.ddf.EscherSimpleProperty; import org.apache.poi.ddf.EscherSpRecord; import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.record.InteractiveInfo; @@ -103,7 +110,7 @@ public abstract class SimpleShape extends Shape { */ public double getLineWidth(){ EscherOptRecord opt = getEscherOptRecord(); - EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH); + EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH); double width = prop == null ? DEFAULT_LINE_WIDTH : (double)prop.getPropertyValue()/EMU_PER_POINT; return width; } @@ -139,7 +146,7 @@ public abstract class SimpleShape extends Shape { public Color getLineColor(){ EscherOptRecord opt = getEscherOptRecord(); - EscherSimpleProperty p = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH); + EscherSimpleProperty p = getEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH); if(p != null && (p.getPropertyValue() & 0x8) == 0) return null; Color clr = getColor(EscherProperties.LINESTYLE__COLOR, EscherProperties.LINESTYLE__OPACITY, -1); @@ -154,7 +161,7 @@ public abstract class SimpleShape extends Shape { public int getLineDashing(){ EscherOptRecord opt = getEscherOptRecord(); - EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__LINEDASHING); + EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEDASHING); return prop == null ? Line.PEN_SOLID : prop.getPropertyValue(); } @@ -186,7 +193,7 @@ public abstract class SimpleShape extends Shape { */ public int getLineStyle(){ EscherOptRecord opt = getEscherOptRecord(); - EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTYLE); + EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTYLE); return prop == null ? Line.LINE_SIMPLE : prop.getPropertyValue(); } @@ -206,47 +213,6 @@ public abstract class SimpleShape extends Shape { getFill().setForegroundColor(color); } - /** - * Whether the shape is horizontally flipped - * - * @return whether the shape is horizontally flipped - */ - public boolean getFlipHorizontal(){ - EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); - return (spRecord.getFlags()& EscherSpRecord.FLAG_FLIPHORIZ) != 0; - } - - /** - * Whether the shape is vertically flipped - * - * @return whether the shape is vertically flipped - */ - public boolean getFlipVertical(){ - EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); - return (spRecord.getFlags()& EscherSpRecord.FLAG_FLIPVERT) != 0; - } - - /** - * Rotation angle in degrees - * - * @return rotation angle in degrees - */ - public int getRotation(){ - int rot = getEscherProperty(EscherProperties.TRANSFORM__ROTATION); - int angle = (rot >> 16) % 360; - - return angle; - } - - /** - * Rotate this shape - * - * @param theta the rotation angle in degrees - */ - public void setRotation(int theta){ - setEscherProperty(EscherProperties.TRANSFORM__ROTATION, (theta << 16)); - } - /** * * @return 'absolute' anchor of this shape relative to the parent sheet @@ -256,17 +222,13 @@ public abstract class SimpleShape extends Shape { //if it is a groupped shape see if we need to transform the coordinates if (_parent != null){ - List lst = new ArrayList(); - lst.add(_parent); - Shape top = _parent; - while(top.getParent() != null) { - top = top.getParent(); - lst.add(top); + ArrayList lst = new ArrayList(); + for (Shape top=this; (top = top.getParent()) != null; ) { + lst.add(0, (ShapeGroup)top); } AffineTransform tx = new AffineTransform(); - for(int i = lst.size() - 1; i >= 0; i--) { - ShapeGroup prnt = (ShapeGroup)lst.get(i); + for(ShapeGroup prnt : lst) { Rectangle2D exterior = prnt.getAnchor2D(); Rectangle2D interior = prnt.getCoordinates(); @@ -276,6 +238,7 @@ public abstract class SimpleShape extends Shape { tx.translate(exterior.getX(), exterior.getY()); tx.scale(scaleX, scaleY); tx.translate(-interior.getX(), -interior.getY()); + } anchor = tx.createTransformedShape(anchor).getBounds2D(); } @@ -314,12 +277,13 @@ public abstract class SimpleShape extends Shape { * * @param recordType type of the record to search */ - protected Record getClientDataRecord(int recordType) { + @SuppressWarnings("unchecked") + protected T getClientDataRecord(int recordType) { Record[] records = getClientRecords(); if(records != null) for (int i = 0; i < records.length; i++) { if(records[i].getRecordType() == recordType){ - return records[i]; + return (T)records[i]; } } return null; @@ -332,7 +296,7 @@ public abstract class SimpleShape extends Shape { */ protected Record[] getClientRecords() { if(_clientData == null){ - EscherRecord r = Shape.getEscherChild(getSpContainer(), EscherClientDataRecord.RECORD_ID); + EscherRecord r = getEscherChild(EscherClientDataRecord.RECORD_ID); //ddf can return EscherContainerRecord with recordId=EscherClientDataRecord.RECORD_ID //convert in to EscherClientDataRecord on the fly if(r != null && !(r instanceof EscherClientDataRecord)){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java b/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java index 79ef820ac8..9e040c1677 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java @@ -88,7 +88,7 @@ public final class Slide extends Sheet // Grab text from SlideListWithTexts entries int i=0; for(i=0; i lst = bstore.getChildRecords(); return ((EscherBSERecord)lst.get(idx-1)).getRef(); } return 0; diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java index 6018cf5f52..b3e4f6ac99 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java @@ -17,38 +17,60 @@ package org.apache.poi.hslf.model; -import junit.framework.TestCase; -import org.apache.poi.hslf.usermodel.SlideShow; -import org.apache.poi.hslf.usermodel.RichTextRun; -import org.apache.poi.hslf.HSLFSlideShow; -import org.apache.poi.ddf.*; -import org.apache.poi.POIDataSamples; - -import java.awt.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.awt.Color; +import java.awt.Dimension; import java.awt.Rectangle; -import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; +import org.apache.poi.POIDataSamples; +import org.apache.poi.ddf.EscherDgRecord; +import org.apache.poi.ddf.EscherDggRecord; +import org.apache.poi.ddf.EscherOptRecord; +import org.apache.poi.ddf.EscherProperties; +import org.apache.poi.ddf.EscherSimpleProperty; +import org.apache.poi.hslf.HSLFSlideShow; +import org.apache.poi.hslf.usermodel.RichTextRun; +import org.apache.poi.hslf.usermodel.SlideShow; +import org.junit.Before; +import org.junit.Test; + /** * Test drawing shapes via Graphics2D * * @author Yegor Kozlov */ -public final class TestShapes extends TestCase { +public final class TestShapes { private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance(); private SlideShow ppt; private SlideShow pptB; - protected void setUp() throws Exception { - ppt = new SlideShow(_slTests.openResourceAsStream("empty.ppt")); - - pptB = new SlideShow(_slTests.openResourceAsStream("empty_textbox.ppt")); + @Before + public void setUp() throws Exception { + InputStream is1 = null, is2 = null; + try { + is1 = _slTests.openResourceAsStream("empty.ppt"); + ppt = new SlideShow(is1); + is2 = _slTests.openResourceAsStream("empty_textbox.ppt"); + pptB = new SlideShow(is2); + } finally { + is1.close(); + is2.close(); + } } - public void testGraphics() throws Exception { + @Test + public void graphics() throws Exception { Slide slide = ppt.createSlide(); Line line = new Line(); @@ -92,7 +114,8 @@ public final class TestShapes extends TestCase { * Verify that we can read TextBox shapes * @throws Exception */ - public void testTextBoxRead() throws Exception { + @Test + public void textBoxRead() throws Exception { ppt = new SlideShow(_slTests.openResourceAsStream("with_textbox.ppt")); Slide sl = ppt.getSlides()[0]; Shape[] sh = sl.getShapes(); @@ -127,7 +150,8 @@ public final class TestShapes extends TestCase { * Verify that we can add TextBox shapes to a slide * and set some of the style attributes */ - public void testTextBoxWriteBytes() throws Exception { + @Test + public void textBoxWriteBytes() throws Exception { ppt = new SlideShow(); Slide sl = ppt.createSlide(); RichTextRun rt; @@ -180,7 +204,8 @@ public final class TestShapes extends TestCase { /** * Test with an empty text box */ - public void testEmptyTextBox() { + @Test + public void emptyTextBox() { assertEquals(2, pptB.getSlides().length); Slide s1 = pptB.getSlides()[0]; Slide s2 = pptB.getSlides()[1]; @@ -194,7 +219,8 @@ public final class TestShapes extends TestCase { * If you iterate over text shapes in a slide and collect them in a set * it must be the same as returned by Slide.getTextRuns(). */ - public void testTextBoxSet() throws Exception { + @Test + public void textBoxSet() throws Exception { textBoxSet("with_textbox.ppt"); textBoxSet("basic_test_ppt_file.ppt"); textBoxSet("next_test_ppt_file.ppt"); @@ -207,13 +233,13 @@ public final class TestShapes extends TestCase { SlideShow ppt = new SlideShow(_slTests.openResourceAsStream(filename)); Slide[] sl = ppt.getSlides(); for (int k = 0; k < sl.length; k++) { - ArrayList lst1 = new ArrayList(); + ArrayList lst1 = new ArrayList(); TextRun[] txt = sl[k].getTextRuns(); for (int i = 0; i < txt.length; i++) { lst1.add(txt[i].getText()); } - ArrayList lst2 = new ArrayList(); + ArrayList lst2 = new ArrayList(); Shape[] sh = sl[k].getShapes(); for (int i = 0; i < sh.length; i++) { if (sh[i] instanceof TextShape){ @@ -229,7 +255,8 @@ public final class TestShapes extends TestCase { /** * Test adding shapes to ShapeGroup */ - public void testShapeGroup() throws Exception { + @Test + public void shapeGroup() throws Exception { SlideShow ppt = new SlideShow(); Slide slide = ppt.createSlide(); @@ -280,7 +307,8 @@ public final class TestShapes extends TestCase { /** * Test functionality of Sheet.removeShape(Shape shape) */ - public void testRemoveShapes() throws IOException { + @Test + public void removeShapes() throws IOException { String file = "with_textbox.ppt"; SlideShow ppt = new SlideShow(_slTests.openResourceAsStream(file)); Slide sl = ppt.getSlides()[0]; @@ -304,21 +332,23 @@ public final class TestShapes extends TestCase { assertEquals("expected 0 shaped in " + file, 0, sl.getShapes().length); } - public void testLineWidth() { + @Test + public void lineWidth() { SimpleShape sh = new AutoShape(ShapeTypes.RightTriangle); - EscherOptRecord opt = (EscherOptRecord)SimpleShape.getEscherChild(sh.getSpContainer(), EscherOptRecord.RECORD_ID); - EscherSimpleProperty prop = (EscherSimpleProperty)SimpleShape.getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH); + EscherOptRecord opt = sh.getEscherOptRecord(); + EscherSimpleProperty prop = SimpleShape.getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH); assertNull(prop); - assertEquals(SimpleShape.DEFAULT_LINE_WIDTH, sh.getLineWidth()); + assertEquals(SimpleShape.DEFAULT_LINE_WIDTH, sh.getLineWidth(), 0); sh.setLineWidth(1.0); - prop = (EscherSimpleProperty)SimpleShape.getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH); + prop = SimpleShape.getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH); assertNotNull(prop); - assertEquals(1.0, sh.getLineWidth()); + assertEquals(1.0, sh.getLineWidth(), 0); } - public void testShapeId() { + @Test + public void shapeId() { SlideShow ppt = new SlideShow(); Slide slide = ppt.createSlide(); Shape shape = null; @@ -367,7 +397,8 @@ public final class TestShapes extends TestCase { assertEquals(numClusters + 1, dgg.getNumIdClusters()); } - public void testLineColor() throws IOException { + @Test + public void lineColor() throws IOException { SlideShow ppt = new SlideShow(_slTests.openResourceAsStream("51731.ppt")); Shape[] shape = ppt.getSlides()[0].getShapes(); @@ -384,11 +415,11 @@ public final class TestShapes extends TestCase { TextShape sh3 = (TextShape)shape[2]; assertEquals("Text in a black border", sh3.getText()); assertEquals(Color.black, sh3.getLineColor()); - assertEquals(0.75, sh3.getLineWidth()); + assertEquals(0.75, sh3.getLineWidth(), 0); TextShape sh4 = (TextShape)shape[3]; assertEquals("Border width is 5 pt", sh4.getText()); assertEquals(Color.black, sh4.getLineColor()); - assertEquals(5.0, sh4.getLineWidth()); + assertEquals(5.0, sh4.getLineWidth(), 0); } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java index d3a759c9bf..67d250a4b6 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java @@ -37,9 +37,14 @@ import java.util.Set; import junit.framework.AssertionFailedError; import org.apache.poi.POIDataSamples; +import org.apache.poi.ddf.EscherArrayProperty; +import org.apache.poi.ddf.EscherColorRef; +import org.apache.poi.ddf.EscherOptRecord; +import org.apache.poi.ddf.EscherProperties; import org.apache.poi.hslf.HSLFSlideShow; import org.apache.poi.hslf.HSLFTestDataSamples; import org.apache.poi.hslf.exceptions.OldPowerPointFormatException; +import org.apache.poi.hslf.model.AutoShape; import org.apache.poi.hslf.model.Background; import org.apache.poi.hslf.model.Fill; import org.apache.poi.hslf.model.HeadersFooters; @@ -59,7 +64,9 @@ import org.apache.poi.hslf.record.Record; import org.apache.poi.hslf.record.SlideListWithText; import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; import org.apache.poi.hslf.record.TextHeaderAtom; +import org.apache.poi.util.LittleEndian; import org.apache.poi.util.StringUtil; +import org.apache.poi.util.Units; import org.junit.Test; /** @@ -625,4 +632,36 @@ public final class TestBugs { inputStream.close(); } } + + @Test + public void bug46441() throws Exception { + InputStream inputStream = new FileInputStream(_slTests.getFile("bug46441.ppt")); + try { + SlideShow slideShow = new SlideShow(inputStream); + AutoShape as = (AutoShape)slideShow.getSlides()[0].getShapes()[0]; + EscherOptRecord opt = as.getEscherOptRecord(); + EscherArrayProperty ep = Shape.getEscherProperty(opt, EscherProperties.FILL__SHADECOLORS); + double exp[][] = { + // r, g, b, position + { 94, 158, 255, 0 }, + { 133, 194, 255, 0.399994 }, + { 196, 214, 235, 0.699997 }, + { 255, 235, 250, 1 } + }; + + int i = 0; + for (byte data[] : ep) { + EscherColorRef ecr = new EscherColorRef(data, 0, 4); + int rgb[] = ecr.getRGB(); + double pos = Units.fixedPointToDecimal(LittleEndian.getInt(data, 4)); + assertEquals((int)exp[i][0], rgb[0]); + assertEquals((int)exp[i][1], rgb[1]); + assertEquals((int)exp[i][2], rgb[2]); + assertEquals(exp[i][3], pos, 0.01); + i++; + } + } finally { + inputStream.close(); + } + } } diff --git a/test-data/slideshow/bug46441.ppt b/test-data/slideshow/bug46441.ppt new file mode 100644 index 0000000000..a7a019c965 Binary files /dev/null and b/test-data/slideshow/bug46441.ppt differ