mirror of https://github.com/apache/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
This commit is contained in:
parent
a361a16899
commit
460678e607
|
@ -17,8 +17,11 @@
|
||||||
|
|
||||||
package org.apache.poi.ddf;
|
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.HexDump;
|
||||||
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Escher array properties are the most wierd construction ever invented
|
* 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)
|
* @author Glen Stampoultzis (glens at superlinksoftware.com)
|
||||||
*/
|
*/
|
||||||
public final class EscherArrayProperty extends EscherComplexProperty {
|
public final class EscherArrayProperty extends EscherComplexProperty implements Iterable<byte[]> {
|
||||||
/**
|
/**
|
||||||
* The size of the header that goes at the
|
* The size of the header that goes at the
|
||||||
* start of the array, before the data
|
* start of the array, before the data
|
||||||
|
@ -205,4 +208,24 @@ public final class EscherArrayProperty extends EscherComplexProperty {
|
||||||
}
|
}
|
||||||
return sizeOfElements;
|
return sizeOfElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterator<byte[]> iterator() {
|
||||||
|
return new Iterator<byte[]>(){
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,4 +40,19 @@ public class Units {
|
||||||
public static double toPoints(long emu){
|
public static double toPoints(long emu){
|
||||||
return (double)emu/EMU_PER_POINT;
|
return (double)emu/EMU_PER_POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a value of type FixedPoint to a decimal number
|
||||||
|
*
|
||||||
|
* @param fixedPoint
|
||||||
|
* @return decimal number
|
||||||
|
*
|
||||||
|
* @see <a href="http://msdn.microsoft.com/en-us/library/dd910765(v=office.12).aspx">[MS-OSHARED] - 2.2.1.6 FixedPoint</a>
|
||||||
|
*/
|
||||||
|
public static double fixedPointToDecimal(int fixedPoint) {
|
||||||
|
int i = (fixedPoint >> 16);
|
||||||
|
int f = (fixedPoint >> 0) & 0xFFFF;
|
||||||
|
double decimal = (i + f/65536.0);
|
||||||
|
return decimal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ public final class ActiveXShape extends Picture {
|
||||||
|
|
||||||
public int getControlIndex(){
|
public int getControlIndex(){
|
||||||
int idx = -1;
|
int idx = -1;
|
||||||
OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);
|
OEShapeAtom oe = getClientDataRecord(RecordTypes.OEShapeAtom.typeID);
|
||||||
if(oe != null) idx = oe.getOptions();
|
if(oe != null) idx = oe.getOptions();
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,20 @@
|
||||||
|
|
||||||
package org.apache.poi.hslf.model;
|
package org.apache.poi.hslf.model;
|
||||||
|
|
||||||
import org.apache.poi.ddf.*;
|
import java.awt.Color;
|
||||||
import org.apache.poi.hslf.record.*;
|
|
||||||
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.util.List;
|
||||||
|
|
||||||
import java.awt.*;
|
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.POILogFactory;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents functionality provided by the 'Fill Effects' dialog in PowerPoint.
|
* Represents functionality provided by the 'Fill Effects' dialog in PowerPoint.
|
||||||
|
@ -112,16 +117,16 @@ public final class Fill {
|
||||||
* @return type of fill
|
* @return type of fill
|
||||||
*/
|
*/
|
||||||
public int getFillType(){
|
public int getFillType(){
|
||||||
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID);
|
EscherOptRecord opt = shape.getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__FILLTYPE);
|
EscherSimpleProperty prop = Shape.getEscherProperty(opt, EscherProperties.FILL__FILLTYPE);
|
||||||
return prop == null ? FILL_SOLID : prop.getPropertyValue();
|
return prop == null ? FILL_SOLID : prop.getPropertyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
protected void afterInsert(Sheet sh){
|
protected void afterInsert(Sheet sh){
|
||||||
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID);
|
EscherOptRecord opt = shape.getEscherOptRecord();
|
||||||
EscherSimpleProperty p = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
|
EscherSimpleProperty p = Shape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
|
||||||
if(p != null) {
|
if(p != null) {
|
||||||
int idx = p.getPropertyValue();
|
int idx = p.getPropertyValue();
|
||||||
EscherBSERecord bse = getEscherBSERecord(idx);
|
EscherBSERecord bse = getEscherBSERecord(idx);
|
||||||
|
@ -138,12 +143,12 @@ public final class Fill {
|
||||||
SlideShow ppt = sheet.getSlideShow();
|
SlideShow ppt = sheet.getSlideShow();
|
||||||
Document doc = ppt.getDocumentRecord();
|
Document doc = ppt.getDocumentRecord();
|
||||||
EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer();
|
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) {
|
if(bstore == null) {
|
||||||
logger.log(POILogger.DEBUG, "EscherContainerRecord.BSTORE_CONTAINER was not found ");
|
logger.log(POILogger.DEBUG, "EscherContainerRecord.BSTORE_CONTAINER was not found ");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List lst = bstore.getChildRecords();
|
List<EscherRecord> lst = bstore.getChildRecords();
|
||||||
return (EscherBSERecord)lst.get(idx-1);
|
return (EscherBSERecord)lst.get(idx-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +159,7 @@ public final class Fill {
|
||||||
* @param type type of the fill
|
* @param type type of the fill
|
||||||
*/
|
*/
|
||||||
public void setFillType(int type){
|
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);
|
Shape.setEscherProperty(opt, EscherProperties.FILL__FILLTYPE, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,8 +167,8 @@ public final class Fill {
|
||||||
* Foreground color
|
* Foreground color
|
||||||
*/
|
*/
|
||||||
public Color getForegroundColor(){
|
public Color getForegroundColor(){
|
||||||
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID);
|
EscherOptRecord opt = shape.getEscherOptRecord();
|
||||||
EscherSimpleProperty p = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
|
EscherSimpleProperty p = Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
|
||||||
|
|
||||||
if(p != null && (p.getPropertyValue() & 0x10) == 0) return null;
|
if(p != null && (p.getPropertyValue() & 0x10) == 0) return null;
|
||||||
|
|
||||||
|
@ -175,7 +180,7 @@ public final class Fill {
|
||||||
* Foreground color
|
* Foreground color
|
||||||
*/
|
*/
|
||||||
public void setForegroundColor(Color color){
|
public void setForegroundColor(Color color){
|
||||||
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID);
|
EscherOptRecord opt = shape.getEscherOptRecord();
|
||||||
if (color == null) {
|
if (color == null) {
|
||||||
Shape.setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150000);
|
Shape.setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150000);
|
||||||
}
|
}
|
||||||
|
@ -190,8 +195,8 @@ public final class Fill {
|
||||||
* Background color
|
* Background color
|
||||||
*/
|
*/
|
||||||
public Color getBackgroundColor(){
|
public Color getBackgroundColor(){
|
||||||
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID);
|
EscherOptRecord opt = shape.getEscherOptRecord();
|
||||||
EscherSimpleProperty p = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
|
EscherSimpleProperty p = Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
|
||||||
|
|
||||||
if(p != null && (p.getPropertyValue() & 0x10) == 0) return null;
|
if(p != null && (p.getPropertyValue() & 0x10) == 0) return null;
|
||||||
|
|
||||||
|
@ -202,7 +207,7 @@ public final class Fill {
|
||||||
* Background color
|
* Background color
|
||||||
*/
|
*/
|
||||||
public void setBackgroundColor(Color color){
|
public void setBackgroundColor(Color color){
|
||||||
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID);
|
EscherOptRecord opt = shape.getEscherOptRecord();
|
||||||
if (color == null) {
|
if (color == null) {
|
||||||
Shape.setEscherProperty(opt, EscherProperties.FILL__FILLBACKCOLOR, -1);
|
Shape.setEscherProperty(opt, EscherProperties.FILL__FILLBACKCOLOR, -1);
|
||||||
}
|
}
|
||||||
|
@ -216,8 +221,8 @@ public final class Fill {
|
||||||
* <code>PictureData</code> object used in a texture, pattern of picture fill.
|
* <code>PictureData</code> object used in a texture, pattern of picture fill.
|
||||||
*/
|
*/
|
||||||
public PictureData getPictureData(){
|
public PictureData getPictureData(){
|
||||||
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID);
|
EscherOptRecord opt = shape.getEscherOptRecord();
|
||||||
EscherSimpleProperty p = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
|
EscherSimpleProperty p = Shape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
|
||||||
if (p == null) return null;
|
if (p == null) return null;
|
||||||
|
|
||||||
SlideShow ppt = shape.getSheet().getSlideShow();
|
SlideShow ppt = shape.getSheet().getSlideShow();
|
||||||
|
@ -225,7 +230,7 @@ public final class Fill {
|
||||||
Document doc = ppt.getDocumentRecord();
|
Document doc = ppt.getDocumentRecord();
|
||||||
|
|
||||||
EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer();
|
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<EscherRecord> lst = bstore.getChildRecords();
|
java.util.List<EscherRecord> lst = bstore.getChildRecords();
|
||||||
int idx = p.getPropertyValue();
|
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 <code>SlideShow.addPicture</code> method.
|
* @param idx 0-based index of the picture added to this ppt by <code>SlideShow.addPicture</code> method.
|
||||||
*/
|
*/
|
||||||
public void setPictureData(int idx){
|
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);
|
Shape.setEscherProperty(opt, (short)(EscherProperties.FILL__PATTERNTEXTURE + 0x4000), idx);
|
||||||
if( idx != 0 ) {
|
if( idx != 0 ) {
|
||||||
if( shape.getSheet() != null ) {
|
if( shape.getSheet() != null ) {
|
||||||
|
|
|
@ -179,11 +179,11 @@ public final class Freeform extends AutoShape {
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 0x4));
|
opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 0x4));
|
||||||
|
|
||||||
EscherArrayProperty verticesProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES + 0x4000));
|
EscherArrayProperty verticesProp = getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES + 0x4000));
|
||||||
if(verticesProp == null) verticesProp = (EscherArrayProperty)getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES);
|
if(verticesProp == null) verticesProp = getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES);
|
||||||
|
|
||||||
EscherArrayProperty segmentsProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO + 0x4000));
|
EscherArrayProperty segmentsProp = getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO + 0x4000));
|
||||||
if(segmentsProp == null) segmentsProp = (EscherArrayProperty)getEscherProperty(opt, EscherProperties.GEOMETRY__SEGMENTINFO);
|
if(segmentsProp == null) segmentsProp = getEscherProperty(opt, EscherProperties.GEOMETRY__SEGMENTINFO);
|
||||||
|
|
||||||
//sanity check
|
//sanity check
|
||||||
if(verticesProp == null) {
|
if(verticesProp == null) {
|
||||||
|
|
|
@ -116,10 +116,10 @@ public final class MovieShape extends Picture {
|
||||||
* @param idx the index of the movie
|
* @param idx the index of the movie
|
||||||
*/
|
*/
|
||||||
public void setMovieIndex(int idx){
|
public void setMovieIndex(int idx){
|
||||||
OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);
|
OEShapeAtom oe = getClientDataRecord(RecordTypes.OEShapeAtom.typeID);
|
||||||
oe.setOptions(idx);
|
oe.setOptions(idx);
|
||||||
|
|
||||||
AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);
|
AnimationInfo an = getClientDataRecord(RecordTypes.AnimationInfo.typeID);
|
||||||
if(an != null) {
|
if(an != null) {
|
||||||
AnimationInfoAtom ai = an.getAnimationInfoAtom();
|
AnimationInfoAtom ai = an.getAnimationInfoAtom();
|
||||||
ai.setDimColor(0x07000000);
|
ai.setDimColor(0x07000000);
|
||||||
|
@ -131,7 +131,7 @@ public final class MovieShape extends Picture {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAutoPlay(boolean flag){
|
public void setAutoPlay(boolean flag){
|
||||||
AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);
|
AnimationInfo an = getClientDataRecord(RecordTypes.AnimationInfo.typeID);
|
||||||
if(an != null){
|
if(an != null){
|
||||||
an.getAnimationInfoAtom().setFlag(AnimationInfoAtom.Automatic, flag);
|
an.getAnimationInfoAtom().setFlag(AnimationInfoAtom.Automatic, flag);
|
||||||
updateClientData();
|
updateClientData();
|
||||||
|
@ -139,7 +139,7 @@ public final class MovieShape extends Picture {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAutoPlay(){
|
public boolean isAutoPlay(){
|
||||||
AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID);
|
AnimationInfo an = getClientDataRecord(RecordTypes.AnimationInfo.typeID);
|
||||||
if(an != null){
|
if(an != null){
|
||||||
return an.getAnimationInfoAtom().getFlag(AnimationInfoAtom.Automatic);
|
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
|
* @return UNC or local path to a video file
|
||||||
*/
|
*/
|
||||||
public String getPath(){
|
public String getPath(){
|
||||||
OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID);
|
OEShapeAtom oe = getClientDataRecord(RecordTypes.OEShapeAtom.typeID);
|
||||||
int idx = oe.getOptions();
|
int idx = oe.getOptions();
|
||||||
|
|
||||||
SlideShow ppt = getSheet().getSlideShow();
|
SlideShow ppt = getSheet().getSlideShow();
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.apache.poi.hslf.usermodel.PictureData;
|
||||||
import org.apache.poi.hslf.usermodel.SlideShow;
|
import org.apache.poi.hslf.usermodel.SlideShow;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
import org.apache.poi.util.StringUtil;
|
import org.apache.poi.util.StringUtil;
|
||||||
|
import org.apache.poi.util.Units;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,7 +121,7 @@ public class Picture extends SimpleShape {
|
||||||
*/
|
*/
|
||||||
public int getPictureIndex(){
|
public int getPictureIndex(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.BLIP__BLIPTODISPLAY);
|
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.BLIP__BLIPTODISPLAY);
|
||||||
return prop == null ? 0 : prop.getPropertyValue();
|
return prop == null ? 0 : prop.getPropertyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +203,7 @@ public class Picture extends SimpleShape {
|
||||||
SlideShow ppt = getSheet().getSlideShow();
|
SlideShow ppt = getSheet().getSlideShow();
|
||||||
Document doc = ppt.getDocumentRecord();
|
Document doc = ppt.getDocumentRecord();
|
||||||
EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer();
|
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) {
|
if(bstore == null) {
|
||||||
logger.log(POILogger.DEBUG, "EscherContainerRecord.BSTORE_CONTAINER was not found ");
|
logger.log(POILogger.DEBUG, "EscherContainerRecord.BSTORE_CONTAINER was not found ");
|
||||||
return null;
|
return null;
|
||||||
|
@ -223,7 +224,7 @@ public class Picture extends SimpleShape {
|
||||||
*/
|
*/
|
||||||
public String getPictureName(){
|
public String getPictureName(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherComplexProperty prop = (EscherComplexProperty)getEscherProperty(opt, EscherProperties.BLIP__BLIPFILENAME);
|
EscherComplexProperty prop = getEscherProperty(opt, EscherProperties.BLIP__BLIPFILENAME);
|
||||||
if (prop == null) return null;
|
if (prop == null) return null;
|
||||||
String name = StringUtil.getFromUnicodeLE(prop.getComplexData());
|
String name = StringUtil.getFromUnicodeLE(prop.getComplexData());
|
||||||
return name.trim();
|
return name.trim();
|
||||||
|
@ -289,16 +290,11 @@ public class Picture extends SimpleShape {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the fractional property or 0 if not defined
|
* @return the fractional property or 0 if not defined
|
||||||
*
|
|
||||||
* @see <a href="http://msdn.microsoft.com/en-us/library/dd910765(v=office.12).aspx">2.2.1.6 FixedPoint</a>
|
|
||||||
*/
|
*/
|
||||||
private static double getFractProp(EscherOptRecord opt, short propertyId) {
|
private static double getFractProp(EscherOptRecord opt, short propertyId) {
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propertyId);
|
EscherSimpleProperty prop = getEscherProperty(opt, propertyId);
|
||||||
if (prop == null) return 0;
|
if (prop == null) return 0;
|
||||||
int fixedPoint = prop.getPropertyValue();
|
int fixedPoint = prop.getPropertyValue();
|
||||||
int i = (fixedPoint >> 16);
|
return Units.fixedPointToDecimal(fixedPoint);
|
||||||
int f = (fixedPoint >> 0) & 0xFFFF;
|
|
||||||
double fp = i + f/65536.0;
|
|
||||||
return fp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,6 +21,7 @@ import org.apache.poi.ddf.*;
|
||||||
import org.apache.poi.hslf.record.ColorSchemeAtom;
|
import org.apache.poi.hslf.record.ColorSchemeAtom;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
import org.apache.poi.util.POILogFactory;
|
import org.apache.poi.util.POILogFactory;
|
||||||
|
import org.apache.poi.util.Units;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
@ -129,7 +130,7 @@ public abstract class Shape {
|
||||||
* @see org.apache.poi.hslf.record.RecordTypes
|
* @see org.apache.poi.hslf.record.RecordTypes
|
||||||
*/
|
*/
|
||||||
public int getShapeType(){
|
public int getShapeType(){
|
||||||
EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
|
EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID);
|
||||||
return spRecord.getShapeType();
|
return spRecord.getShapeType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +139,7 @@ public abstract class Shape {
|
||||||
* @see org.apache.poi.hslf.record.RecordTypes
|
* @see org.apache.poi.hslf.record.RecordTypes
|
||||||
*/
|
*/
|
||||||
public void setShapeType(int type){
|
public void setShapeType(int type){
|
||||||
EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
|
EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID);
|
||||||
spRecord.setShapeType( (short) type );
|
spRecord.setShapeType( (short) type );
|
||||||
spRecord.setVersion( (short) 0x2 );
|
spRecord.setVersion( (short) 0x2 );
|
||||||
}
|
}
|
||||||
|
@ -161,15 +162,15 @@ public abstract class Shape {
|
||||||
* @return the anchor of this shape
|
* @return the anchor of this shape
|
||||||
*/
|
*/
|
||||||
public Rectangle2D getAnchor2D(){
|
public Rectangle2D getAnchor2D(){
|
||||||
EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
|
EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID);
|
||||||
int flags = spRecord.getFlags();
|
int flags = spRecord.getFlags();
|
||||||
Rectangle2D anchor=null;
|
Rectangle2D anchor=null;
|
||||||
if ((flags & EscherSpRecord.FLAG_CHILD) != 0){
|
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();
|
anchor = new java.awt.Rectangle();
|
||||||
if(rec == null){
|
if(rec == null){
|
||||||
logger.log(POILogger.WARN, "EscherSpRecord.FLAG_CHILD is set but EscherChildAnchorRecord was not found");
|
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 java.awt.Rectangle();
|
||||||
anchor = new Rectangle2D.Float(
|
anchor = new Rectangle2D.Float(
|
||||||
(float)clrec.getCol1()*POINT_DPI/MASTER_DPI,
|
(float)clrec.getCol1()*POINT_DPI/MASTER_DPI,
|
||||||
|
@ -187,7 +188,7 @@ public abstract class Shape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID);
|
EscherClientAnchorRecord rec = getEscherChild(EscherClientAnchorRecord.RECORD_ID);
|
||||||
anchor = new java.awt.Rectangle();
|
anchor = new java.awt.Rectangle();
|
||||||
anchor = new Rectangle2D.Float(
|
anchor = new Rectangle2D.Float(
|
||||||
(float)rec.getCol1()*POINT_DPI/MASTER_DPI,
|
(float)rec.getCol1()*POINT_DPI/MASTER_DPI,
|
||||||
|
@ -210,17 +211,17 @@ public abstract class Shape {
|
||||||
* @param anchor new anchor
|
* @param anchor new anchor
|
||||||
*/
|
*/
|
||||||
public void setAnchor(Rectangle2D anchor){
|
public void setAnchor(Rectangle2D anchor){
|
||||||
EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
|
EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID);
|
||||||
int flags = spRecord.getFlags();
|
int flags = spRecord.getFlags();
|
||||||
if ((flags & EscherSpRecord.FLAG_CHILD) != 0){
|
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.setDx1((int)(anchor.getX()*MASTER_DPI/POINT_DPI));
|
||||||
rec.setDy1((int)(anchor.getY()*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.setDx2((int)((anchor.getWidth() + anchor.getX())*MASTER_DPI/POINT_DPI));
|
||||||
rec.setDy2((int)((anchor.getHeight() + anchor.getY())*MASTER_DPI/POINT_DPI));
|
rec.setDy2((int)((anchor.getHeight() + anchor.getY())*MASTER_DPI/POINT_DPI));
|
||||||
}
|
}
|
||||||
else {
|
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.setFlag((short)(anchor.getY()*MASTER_DPI/POINT_DPI));
|
||||||
rec.setCol1((short)(anchor.getX()*MASTER_DPI/POINT_DPI));
|
rec.setCol1((short)(anchor.getX()*MASTER_DPI/POINT_DPI));
|
||||||
rec.setDx1((short)(((anchor.getWidth() + 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 <code>null</code> if not found.
|
* @return escher record or <code>null</code> if not found.
|
||||||
*/
|
*/
|
||||||
public static EscherRecord getEscherChild(EscherContainerRecord owner, int recordId){
|
public static <T extends EscherRecord> T getEscherChild(EscherContainerRecord owner, int recordId){
|
||||||
for ( Iterator<EscherRecord> iterator = owner.getChildIterator(); iterator.hasNext(); )
|
return owner.getChildById((short)recordId);
|
||||||
{
|
|
||||||
EscherRecord escherRecord = iterator.next();
|
|
||||||
if (escherRecord.getRecordId() == recordId)
|
|
||||||
return escherRecord;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T extends EscherRecord> T getEscherChild(int recordId){
|
||||||
|
return _escherContainer.getChildById((short)recordId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns escher property by id.
|
* Returns escher property by id.
|
||||||
*
|
*
|
||||||
* @return escher property or <code>null</code> if not found.
|
* @return escher property or <code>null</code> if not found.
|
||||||
*/
|
*/
|
||||||
public static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){
|
public static <T extends EscherProperty> T getEscherProperty(EscherOptRecord opt, int propId){
|
||||||
if(opt != null) for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); )
|
return opt.lookup(propId);
|
||||||
{
|
|
||||||
EscherProperty prop = (EscherProperty) iterator.next();
|
|
||||||
if (prop.getPropertyNumber() == propId)
|
|
||||||
return prop;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -279,11 +272,11 @@ public abstract class Shape {
|
||||||
* @param value value of the property. If value = -1 then the property is removed.
|
* @param value value of the property. If value = -1 then the property is removed.
|
||||||
*/
|
*/
|
||||||
public static void setEscherProperty(EscherOptRecord opt, short propId, int value){
|
public static void setEscherProperty(EscherOptRecord opt, short propId, int value){
|
||||||
java.util.List props = opt.getEscherProperties();
|
java.util.List<EscherProperty> props = opt.getEscherProperties();
|
||||||
for ( Iterator iterator = props.iterator(); iterator.hasNext(); ) {
|
for ( Iterator<EscherProperty> iterator = props.iterator(); iterator.hasNext(); ) {
|
||||||
EscherProperty prop = (EscherProperty) iterator.next();
|
if (iterator.next().getPropertyNumber() == propId){
|
||||||
if (prop.getId() == propId){
|
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value != -1) {
|
if (value != -1) {
|
||||||
|
@ -310,7 +303,7 @@ public abstract class Shape {
|
||||||
*/
|
*/
|
||||||
public int getEscherProperty(short propId){
|
public int getEscherProperty(short propId){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propId);
|
EscherSimpleProperty prop = getEscherProperty(opt, propId);
|
||||||
return prop == null ? 0 : prop.getPropertyValue();
|
return prop == null ? 0 : prop.getPropertyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +314,7 @@ public abstract class Shape {
|
||||||
*/
|
*/
|
||||||
public int getEscherProperty(short propId, int defaultValue){
|
public int getEscherProperty(short propId, int defaultValue){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propId);
|
EscherSimpleProperty prop = getEscherProperty(opt, propId);
|
||||||
return prop == null ? defaultValue : prop.getPropertyValue();
|
return prop == null ? defaultValue : prop.getPropertyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,32 +358,30 @@ public abstract class Shape {
|
||||||
|
|
||||||
Color getColor(short colorProperty, short opacityProperty, int defaultColor){
|
Color getColor(short colorProperty, short opacityProperty, int defaultColor){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty p = (EscherSimpleProperty)getEscherProperty(opt, colorProperty);
|
EscherSimpleProperty p = getEscherProperty(opt, colorProperty);
|
||||||
if(p == null && defaultColor == -1) return null;
|
if(p == null && defaultColor == -1) return null;
|
||||||
|
|
||||||
int val = p == null ? defaultColor : p.getPropertyValue();
|
int val = (p == null) ? defaultColor : p.getPropertyValue();
|
||||||
|
|
||||||
int a = (val >> 24) & 0xFF;
|
EscherColorRef ecr = new EscherColorRef(val);
|
||||||
int b = (val >> 16) & 0xFF;
|
|
||||||
int g = (val >> 8) & 0xFF;
|
boolean fPaletteIndex = ecr.hasPaletteIndexFlag();
|
||||||
int r = (val >> 0) & 0xFF;
|
boolean fPaletteRGB = ecr.hasPaletteRGBFlag();
|
||||||
|
boolean fSystemRGB = ecr.hasSystemRGBFlag();
|
||||||
boolean fPaletteIndex = (a & 1) != 0;
|
boolean fSchemeIndex = ecr.hasSchemeIndexFlag();
|
||||||
boolean fPaletteRGB = (a & (1 << 1)) != 0;
|
boolean fSysIndex = ecr.hasSysIndexFlag();
|
||||||
boolean fSystemRGB = (a & (1 << 2)) != 0;
|
|
||||||
boolean fSchemeIndex = (a & (1 << 3)) != 0;
|
int rgb[] = ecr.getRGB();
|
||||||
boolean fSysIndex = (a & (1 << 4)) != 0;
|
|
||||||
|
|
||||||
Sheet sheet = getSheet();
|
Sheet sheet = getSheet();
|
||||||
if (fSchemeIndex && sheet != null)
|
if (fSchemeIndex && sheet != null) {
|
||||||
{
|
|
||||||
//red is the index to the color scheme
|
//red is the index to the color scheme
|
||||||
ColorSchemeAtom ca = sheet.getColorScheme();
|
ColorSchemeAtom ca = sheet.getColorScheme();
|
||||||
int schemeColor = ca.getColor(r);
|
int schemeColor = ca.getColor(ecr.getSchemeIndex());
|
||||||
|
|
||||||
r = (schemeColor >> 0) & 0xFF;
|
rgb[0] = (schemeColor >> 0) & 0xFF;
|
||||||
g = (schemeColor >> 8) & 0xFF;
|
rgb[1] = (schemeColor >> 8) & 0xFF;
|
||||||
b = (schemeColor >> 16) & 0xFF;
|
rgb[2] = (schemeColor >> 16) & 0xFF;
|
||||||
} else if (fPaletteIndex){
|
} else if (fPaletteIndex){
|
||||||
//TODO
|
//TODO
|
||||||
} else if (fPaletteRGB){
|
} else if (fPaletteRGB){
|
||||||
|
@ -401,13 +392,11 @@ public abstract class Shape {
|
||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
EscherSimpleProperty op = (EscherSimpleProperty)getEscherProperty(opt, opacityProperty);
|
EscherSimpleProperty op = getEscherProperty(opt, opacityProperty);
|
||||||
int defaultOpacity = 0x00010000;
|
int defaultOpacity = 0x00010000;
|
||||||
int opacity = op == null ? defaultOpacity : op.getPropertyValue();
|
int opacity = (op == null) ? defaultOpacity : op.getPropertyValue();
|
||||||
int i = (opacity >> 16);
|
double alpha = Units.fixedPointToDecimal(opacity)*255.0;
|
||||||
int f = (opacity >> 0) & 0xFFFF ;
|
return new Color(rgb[0], rgb[1], rgb[2], (int)alpha);
|
||||||
double alpha = (i + f/65536.0)*255;
|
|
||||||
return new Color(r, g, b, (int)alpha);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Color toRGB(int val){
|
Color toRGB(int val){
|
||||||
|
@ -436,7 +425,7 @@ public abstract class Shape {
|
||||||
* @return id for the shape.
|
* @return id for the shape.
|
||||||
*/
|
*/
|
||||||
public int getShapeId(){
|
public int getShapeId(){
|
||||||
EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
|
EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID);
|
||||||
return spRecord == null ? 0 : spRecord.getShapeId();
|
return spRecord == null ? 0 : spRecord.getShapeId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +435,7 @@ public abstract class Shape {
|
||||||
* @param id of the shape
|
* @param id of the shape
|
||||||
*/
|
*/
|
||||||
public void setShapeId(int id){
|
public void setShapeId(int id){
|
||||||
EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
|
EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID);
|
||||||
if(spRecord != null) spRecord.setShapeId(id);
|
if(spRecord != null) spRecord.setShapeId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +473,48 @@ public abstract class Shape {
|
||||||
return getLogicalAnchor2D();
|
return getLogicalAnchor2D();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected EscherOptRecord getEscherOptRecord() {
|
public EscherOptRecord getEscherOptRecord() {
|
||||||
return (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ public final class ShapeFactory {
|
||||||
if(opt != null){
|
if(opt != null){
|
||||||
try {
|
try {
|
||||||
EscherPropertyFactory f = new EscherPropertyFactory();
|
EscherPropertyFactory f = new EscherPropertyFactory();
|
||||||
List props = f.createProperties( opt.serialize(), 8, opt.getInstance() );
|
List<EscherProperty> props = f.createProperties( opt.serialize(), 8, opt.getInstance() );
|
||||||
EscherSimpleProperty p = (EscherSimpleProperty)props.get(0);
|
EscherSimpleProperty p = (EscherSimpleProperty)props.get(0);
|
||||||
if(p.getPropertyNumber() == 0x39F && p.getPropertyValue() == 1){
|
if(p.getPropertyNumber() == 0x39F && p.getPropertyValue() == 1){
|
||||||
group = new Table(spContainer, parent);
|
group = new Table(spContainer, parent);
|
||||||
|
@ -91,8 +91,8 @@ public final class ShapeFactory {
|
||||||
break;
|
break;
|
||||||
case ShapeTypes.HostControl:
|
case ShapeTypes.HostControl:
|
||||||
case ShapeTypes.PictureFrame: {
|
case ShapeTypes.PictureFrame: {
|
||||||
InteractiveInfo info = (InteractiveInfo)getClientDataRecord(spContainer, RecordTypes.InteractiveInfo.typeID);
|
InteractiveInfo info = getClientDataRecord(spContainer, RecordTypes.InteractiveInfo.typeID);
|
||||||
OEShapeAtom oes = (OEShapeAtom)getClientDataRecord(spContainer, RecordTypes.OEShapeAtom.typeID);
|
OEShapeAtom oes = getClientDataRecord(spContainer, RecordTypes.OEShapeAtom.typeID);
|
||||||
if(info != null && info.getInteractiveInfoAtom() != null){
|
if(info != null && info.getInteractiveInfoAtom() != null){
|
||||||
switch(info.getInteractiveInfoAtom().getAction()){
|
switch(info.getInteractiveInfoAtom().getAction()){
|
||||||
case InteractiveInfoAtom.ACTION_OLE:
|
case InteractiveInfoAtom.ACTION_OLE:
|
||||||
|
@ -115,7 +115,7 @@ public final class ShapeFactory {
|
||||||
shape = new Line(spContainer, parent);
|
shape = new Line(spContainer, parent);
|
||||||
break;
|
break;
|
||||||
case ShapeTypes.NotPrimitive: {
|
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);
|
EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES);
|
||||||
if(prop != null)
|
if(prop != null)
|
||||||
shape = new Freeform(spContainer, parent);
|
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 extends Record> T getClientDataRecord(EscherContainerRecord spContainer, int recordType) {
|
||||||
Record oep = null;
|
Record oep = null;
|
||||||
for (Iterator<EscherRecord> it = spContainer.getChildIterator(); it.hasNext();) {
|
for (Iterator<EscherRecord> it = spContainer.getChildIterator(); it.hasNext();) {
|
||||||
EscherRecord obj = it.next();
|
EscherRecord obj = it.next();
|
||||||
|
@ -143,12 +144,12 @@ public final class ShapeFactory {
|
||||||
Record[] records = Record.findChildRecords(data, 8, data.length - 8);
|
Record[] records = Record.findChildRecords(data, 8, data.length - 8);
|
||||||
for (int j = 0; j < records.length; j++) {
|
for (int j = 0; j < records.length; j++) {
|
||||||
if (records[j].getRecordType() == recordType) {
|
if (records[j].getRecordType() == recordType) {
|
||||||
return records[j];
|
return (T)records[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return oep;
|
return (T)oep;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,9 +100,7 @@ public class ShapeGroup extends Shape{
|
||||||
*/
|
*/
|
||||||
public void setAnchor(java.awt.Rectangle anchor){
|
public void setAnchor(java.awt.Rectangle anchor){
|
||||||
|
|
||||||
EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChild(0);
|
EscherClientAnchorRecord clientAnchor = getEscherChild(EscherClientAnchorRecord.RECORD_ID);
|
||||||
|
|
||||||
EscherClientAnchorRecord clientAnchor = (EscherClientAnchorRecord)getEscherChild(spContainer, EscherClientAnchorRecord.RECORD_ID);
|
|
||||||
//hack. internal variable EscherClientAnchorRecord.shortRecord can be
|
//hack. internal variable EscherClientAnchorRecord.shortRecord can be
|
||||||
//initialized only in fillFields(). We need to set shortRecord=false;
|
//initialized only in fillFields(). We need to set shortRecord=false;
|
||||||
byte[] header = new byte[16];
|
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.setDx1((short)((anchor.width + anchor.x)*MASTER_DPI/POINT_DPI));
|
||||||
clientAnchor.setRow1((short)((anchor.height + anchor.y)*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.setRectX1(anchor.x*MASTER_DPI/POINT_DPI);
|
||||||
spgr.setRectY1(anchor.y*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
|
* @param anchor the coordinate space of this group
|
||||||
*/
|
*/
|
||||||
public void setCoordinates(Rectangle2D anchor){
|
public void setCoordinates(Rectangle2D anchor){
|
||||||
EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChild(0);
|
EscherSpgrRecord spgr = getEscherChild(EscherSpgrRecord.RECORD_ID);
|
||||||
EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(spContainer, EscherSpgrRecord.RECORD_ID);
|
|
||||||
|
|
||||||
int x1 = (int)Math.round(anchor.getX()*MASTER_DPI/POINT_DPI);
|
int x1 = (int)Math.round(anchor.getX()*MASTER_DPI/POINT_DPI);
|
||||||
int y1 = (int)Math.round(anchor.getY()*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
|
* @return the coordinate space of this group
|
||||||
*/
|
*/
|
||||||
public Rectangle2D getCoordinates(){
|
public Rectangle2D getCoordinates(){
|
||||||
EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChild(0);
|
EscherSpgrRecord spgr = getEscherChild(EscherSpgrRecord.RECORD_ID);
|
||||||
EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(spContainer, EscherSpgrRecord.RECORD_ID);
|
|
||||||
|
|
||||||
Rectangle2D.Float anchor = new Rectangle2D.Float();
|
Rectangle2D.Float anchor = new Rectangle2D.Float();
|
||||||
anchor.x = (float)spgr.getRectX1()*POINT_DPI/MASTER_DPI;
|
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
|
* @return the anchor of this shape group
|
||||||
*/
|
*/
|
||||||
public Rectangle2D getAnchor2D(){
|
public Rectangle2D getAnchor2D(){
|
||||||
EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChild(0);
|
EscherClientAnchorRecord clientAnchor = getEscherChild(EscherClientAnchorRecord.RECORD_ID);
|
||||||
EscherClientAnchorRecord clientAnchor = (EscherClientAnchorRecord)getEscherChild(spContainer, EscherClientAnchorRecord.RECORD_ID);
|
|
||||||
Rectangle2D.Float anchor = new Rectangle2D.Float();
|
Rectangle2D.Float anchor = new Rectangle2D.Float();
|
||||||
if(clientAnchor == null){
|
if(clientAnchor == null){
|
||||||
logger.log(POILogger.INFO, "EscherClientAnchorRecord was not found for shape group. Searching for EscherChildAnchorRecord.");
|
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(
|
anchor = new Rectangle2D.Float(
|
||||||
(float)rec.getDx1()*POINT_DPI/MASTER_DPI,
|
(float)rec.getDx1()*POINT_DPI/MASTER_DPI,
|
||||||
(float)rec.getDy1()*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.
|
* @return type of the shape.
|
||||||
*/
|
*/
|
||||||
public int getShapeType(){
|
public int getShapeType(){
|
||||||
EscherContainerRecord groupInfoContainer = (EscherContainerRecord)_escherContainer.getChild(0);
|
EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID);
|
||||||
EscherSpRecord spRecord = groupInfoContainer.getChildById(EscherSpRecord.RECORD_ID);
|
|
||||||
return spRecord.getOptions() >> 4;
|
return spRecord.getOptions() >> 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,4 +285,10 @@ public class ShapeGroup extends Shape{
|
||||||
|
|
||||||
graphics.setTransform(at);
|
graphics.setTransform(at);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends EscherRecord> T getEscherChild(int recordId){
|
||||||
|
EscherContainerRecord groupInfoContainer = (EscherContainerRecord)_escherContainer.getChild(0);
|
||||||
|
return groupInfoContainer.getChildById((short)recordId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -484,7 +484,7 @@ public abstract class Sheet {
|
||||||
placeholderId = oep.getPlaceholderId();
|
placeholderId = oep.getPlaceholderId();
|
||||||
} else {
|
} else {
|
||||||
//special case for files saved in Office 2007
|
//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(hldr != null) placeholderId = hldr.getPlaceholderId();
|
||||||
}
|
}
|
||||||
if(placeholderId == type){
|
if(placeholderId == type){
|
||||||
|
|
|
@ -23,9 +23,16 @@ import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.util.ArrayList;
|
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.ddf.EscherSpRecord;
|
||||||
import org.apache.poi.hslf.exceptions.HSLFException;
|
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||||
import org.apache.poi.hslf.record.InteractiveInfo;
|
import org.apache.poi.hslf.record.InteractiveInfo;
|
||||||
|
@ -103,7 +110,7 @@ public abstract class SimpleShape extends Shape {
|
||||||
*/
|
*/
|
||||||
public double getLineWidth(){
|
public double getLineWidth(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
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;
|
double width = prop == null ? DEFAULT_LINE_WIDTH : (double)prop.getPropertyValue()/EMU_PER_POINT;
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
@ -139,7 +146,7 @@ public abstract class SimpleShape extends Shape {
|
||||||
public Color getLineColor(){
|
public Color getLineColor(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
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;
|
if(p != null && (p.getPropertyValue() & 0x8) == 0) return null;
|
||||||
|
|
||||||
Color clr = getColor(EscherProperties.LINESTYLE__COLOR, EscherProperties.LINESTYLE__OPACITY, -1);
|
Color clr = getColor(EscherProperties.LINESTYLE__COLOR, EscherProperties.LINESTYLE__OPACITY, -1);
|
||||||
|
@ -154,7 +161,7 @@ public abstract class SimpleShape extends Shape {
|
||||||
public int getLineDashing(){
|
public int getLineDashing(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
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();
|
return prop == null ? Line.PEN_SOLID : prop.getPropertyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +193,7 @@ public abstract class SimpleShape extends Shape {
|
||||||
*/
|
*/
|
||||||
public int getLineStyle(){
|
public int getLineStyle(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
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();
|
return prop == null ? Line.LINE_SIMPLE : prop.getPropertyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,47 +213,6 @@ public abstract class SimpleShape extends Shape {
|
||||||
getFill().setForegroundColor(color);
|
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
|
* @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 it is a groupped shape see if we need to transform the coordinates
|
||||||
if (_parent != null){
|
if (_parent != null){
|
||||||
List<Shape> lst = new ArrayList<Shape>();
|
ArrayList<ShapeGroup> lst = new ArrayList<ShapeGroup>();
|
||||||
lst.add(_parent);
|
for (Shape top=this; (top = top.getParent()) != null; ) {
|
||||||
Shape top = _parent;
|
lst.add(0, (ShapeGroup)top);
|
||||||
while(top.getParent() != null) {
|
|
||||||
top = top.getParent();
|
|
||||||
lst.add(top);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AffineTransform tx = new AffineTransform();
|
AffineTransform tx = new AffineTransform();
|
||||||
for(int i = lst.size() - 1; i >= 0; i--) {
|
for(ShapeGroup prnt : lst) {
|
||||||
ShapeGroup prnt = (ShapeGroup)lst.get(i);
|
|
||||||
Rectangle2D exterior = prnt.getAnchor2D();
|
Rectangle2D exterior = prnt.getAnchor2D();
|
||||||
Rectangle2D interior = prnt.getCoordinates();
|
Rectangle2D interior = prnt.getCoordinates();
|
||||||
|
|
||||||
|
@ -276,6 +238,7 @@ public abstract class SimpleShape extends Shape {
|
||||||
tx.translate(exterior.getX(), exterior.getY());
|
tx.translate(exterior.getX(), exterior.getY());
|
||||||
tx.scale(scaleX, scaleY);
|
tx.scale(scaleX, scaleY);
|
||||||
tx.translate(-interior.getX(), -interior.getY());
|
tx.translate(-interior.getX(), -interior.getY());
|
||||||
|
|
||||||
}
|
}
|
||||||
anchor = tx.createTransformedShape(anchor).getBounds2D();
|
anchor = tx.createTransformedShape(anchor).getBounds2D();
|
||||||
}
|
}
|
||||||
|
@ -314,12 +277,13 @@ public abstract class SimpleShape extends Shape {
|
||||||
*
|
*
|
||||||
* @param recordType type of the record to search
|
* @param recordType type of the record to search
|
||||||
*/
|
*/
|
||||||
protected Record getClientDataRecord(int recordType) {
|
@SuppressWarnings("unchecked")
|
||||||
|
protected <T extends Record> T getClientDataRecord(int recordType) {
|
||||||
|
|
||||||
Record[] records = getClientRecords();
|
Record[] records = getClientRecords();
|
||||||
if(records != null) for (int i = 0; i < records.length; i++) {
|
if(records != null) for (int i = 0; i < records.length; i++) {
|
||||||
if(records[i].getRecordType() == recordType){
|
if(records[i].getRecordType() == recordType){
|
||||||
return records[i];
|
return (T)records[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -332,7 +296,7 @@ public abstract class SimpleShape extends Shape {
|
||||||
*/
|
*/
|
||||||
protected Record[] getClientRecords() {
|
protected Record[] getClientRecords() {
|
||||||
if(_clientData == null){
|
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
|
//ddf can return EscherContainerRecord with recordId=EscherClientDataRecord.RECORD_ID
|
||||||
//convert in to EscherClientDataRecord on the fly
|
//convert in to EscherClientDataRecord on the fly
|
||||||
if(r != null && !(r instanceof EscherClientDataRecord)){
|
if(r != null && !(r instanceof EscherClientDataRecord)){
|
||||||
|
|
|
@ -88,7 +88,7 @@ public final class Slide extends Sheet
|
||||||
// Grab text from SlideListWithTexts entries
|
// Grab text from SlideListWithTexts entries
|
||||||
int i=0;
|
int i=0;
|
||||||
for(i=0; i<textRuns.size(); i++) {
|
for(i=0; i<textRuns.size(); i++) {
|
||||||
_runs[i] = (TextRun)textRuns.get(i);
|
_runs[i] = textRuns.get(i);
|
||||||
_runs[i].setSheet(this);
|
_runs[i].setSheet(this);
|
||||||
}
|
}
|
||||||
// Grab text from slide's PPDrawing
|
// Grab text from slide's PPDrawing
|
||||||
|
|
|
@ -225,7 +225,7 @@ public abstract class TextShape extends SimpleShape {
|
||||||
|
|
||||||
protected EscherTextboxWrapper getEscherTextboxWrapper(){
|
protected EscherTextboxWrapper getEscherTextboxWrapper(){
|
||||||
if(_txtbox == null){
|
if(_txtbox == null){
|
||||||
EscherTextboxRecord textRecord = (EscherTextboxRecord)Shape.getEscherChild(_escherContainer, EscherTextboxRecord.RECORD_ID);
|
EscherTextboxRecord textRecord = getEscherChild(EscherTextboxRecord.RECORD_ID);
|
||||||
if(textRecord != null) _txtbox = new EscherTextboxWrapper(textRecord);
|
if(textRecord != null) _txtbox = new EscherTextboxWrapper(textRecord);
|
||||||
}
|
}
|
||||||
return _txtbox;
|
return _txtbox;
|
||||||
|
@ -281,7 +281,7 @@ public abstract class TextShape extends SimpleShape {
|
||||||
*/
|
*/
|
||||||
public int getVerticalAlignment(){
|
public int getVerticalAlignment(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT);
|
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT);
|
||||||
int valign = TextShape.AnchorTop;
|
int valign = TextShape.AnchorTop;
|
||||||
if (prop == null){
|
if (prop == null){
|
||||||
/**
|
/**
|
||||||
|
@ -352,7 +352,7 @@ public abstract class TextShape extends SimpleShape {
|
||||||
*/
|
*/
|
||||||
public float getMarginBottom(){
|
public float getMarginBottom(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM);
|
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM);
|
||||||
int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue();
|
int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue();
|
||||||
return (float)val/EMU_PER_POINT;
|
return (float)val/EMU_PER_POINT;
|
||||||
}
|
}
|
||||||
|
@ -377,7 +377,7 @@ public abstract class TextShape extends SimpleShape {
|
||||||
*/
|
*/
|
||||||
public float getMarginLeft(){
|
public float getMarginLeft(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTLEFT);
|
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__TEXTLEFT);
|
||||||
int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue();
|
int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue();
|
||||||
return (float)val/EMU_PER_POINT;
|
return (float)val/EMU_PER_POINT;
|
||||||
}
|
}
|
||||||
|
@ -402,7 +402,7 @@ public abstract class TextShape extends SimpleShape {
|
||||||
*/
|
*/
|
||||||
public float getMarginRight(){
|
public float getMarginRight(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTRIGHT);
|
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__TEXTRIGHT);
|
||||||
int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue();
|
int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue();
|
||||||
return (float)val/EMU_PER_POINT;
|
return (float)val/EMU_PER_POINT;
|
||||||
}
|
}
|
||||||
|
@ -426,7 +426,7 @@ public abstract class TextShape extends SimpleShape {
|
||||||
*/
|
*/
|
||||||
public float getMarginTop(){
|
public float getMarginTop(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTTOP);
|
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__TEXTTOP);
|
||||||
int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue();
|
int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue();
|
||||||
return (float)val/EMU_PER_POINT;
|
return (float)val/EMU_PER_POINT;
|
||||||
}
|
}
|
||||||
|
@ -450,7 +450,7 @@ public abstract class TextShape extends SimpleShape {
|
||||||
*/
|
*/
|
||||||
public int getWordWrap(){
|
public int getWordWrap(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__WRAPTEXT);
|
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__WRAPTEXT);
|
||||||
return prop == null ? WrapSquare : prop.getPropertyValue();
|
return prop == null ? WrapSquare : prop.getPropertyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,7 +469,7 @@ public abstract class TextShape extends SimpleShape {
|
||||||
*/
|
*/
|
||||||
public int getTextId(){
|
public int getTextId(){
|
||||||
EscherOptRecord opt = getEscherOptRecord();
|
EscherOptRecord opt = getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTID);
|
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__TEXTID);
|
||||||
return prop == null ? 0 : prop.getPropertyValue();
|
return prop == null ? 0 : prop.getPropertyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,7 +561,7 @@ public abstract class TextShape extends SimpleShape {
|
||||||
logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
|
logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
EscherSpRecord escherSpRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
|
EscherSpRecord escherSpRecord = getEscherChild(EscherSpRecord.RECORD_ID);
|
||||||
int shapeId = escherSpRecord.getShapeId();
|
int shapeId = escherSpRecord.getShapeId();
|
||||||
if(runs != null) for (int i = 0; i < runs.length; i++) {
|
if(runs != null) for (int i = 0; i < runs.length; i++) {
|
||||||
if(runs[i].getShapeId() == shapeId){
|
if(runs[i].getShapeId() == shapeId){
|
||||||
|
@ -593,7 +593,7 @@ public abstract class TextShape extends SimpleShape {
|
||||||
* @return <code>OEPlaceholderAtom</code> or <code>null</code> if not found
|
* @return <code>OEPlaceholderAtom</code> or <code>null</code> if not found
|
||||||
*/
|
*/
|
||||||
public OEPlaceholderAtom getPlaceholderAtom(){
|
public OEPlaceholderAtom getPlaceholderAtom(){
|
||||||
return (OEPlaceholderAtom)getClientDataRecord(RecordTypes.OEPlaceholderAtom.typeID);
|
return getClientDataRecord(RecordTypes.OEPlaceholderAtom.typeID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,17 +17,25 @@
|
||||||
|
|
||||||
package org.apache.poi.hslf.model;
|
package org.apache.poi.hslf.model;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.*;
|
import java.awt.Color;
|
||||||
import java.awt.*;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.poi.ddf.*;
|
import org.apache.poi.POIDataSamples;
|
||||||
|
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.HSLFSlideShow;
|
||||||
import org.apache.poi.hslf.record.Document;
|
import org.apache.poi.hslf.record.Document;
|
||||||
import org.apache.poi.hslf.usermodel.SlideShow;
|
import org.apache.poi.hslf.usermodel.SlideShow;
|
||||||
import org.apache.poi.hslf.HSLFSlideShow;
|
import org.junit.Test;
|
||||||
import org.apache.poi.POIDataSamples;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,13 +43,14 @@ import org.apache.poi.POIDataSamples;
|
||||||
*
|
*
|
||||||
* @author Yegor Kozlov
|
* @author Yegor Kozlov
|
||||||
*/
|
*/
|
||||||
public final class TestBackground extends TestCase {
|
public final class TestBackground {
|
||||||
private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
|
private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default background for slide, shape and slide master.
|
* Default background for slide, shape and slide master.
|
||||||
*/
|
*/
|
||||||
public void testDefaults() {
|
@Test
|
||||||
|
public void defaults() {
|
||||||
SlideShow ppt = new SlideShow();
|
SlideShow ppt = new SlideShow();
|
||||||
|
|
||||||
assertEquals(Fill.FILL_SOLID, ppt.getSlidesMasters()[0].getBackground().getFill().getFillType());
|
assertEquals(Fill.FILL_SOLID, ppt.getSlidesMasters()[0].getBackground().getFill().getFillType());
|
||||||
|
@ -57,7 +66,8 @@ public final class TestBackground extends TestCase {
|
||||||
/**
|
/**
|
||||||
* Read fill information from an reference ppt file
|
* Read fill information from an reference ppt file
|
||||||
*/
|
*/
|
||||||
public void testReadBackground() throws Exception {
|
@Test
|
||||||
|
public void readBackground() throws Exception {
|
||||||
SlideShow ppt = new SlideShow(_slTests.openResourceAsStream("backgrounds.ppt"));
|
SlideShow ppt = new SlideShow(_slTests.openResourceAsStream("backgrounds.ppt"));
|
||||||
Fill fill;
|
Fill fill;
|
||||||
Shape shape;
|
Shape shape;
|
||||||
|
@ -88,7 +98,8 @@ public final class TestBackground extends TestCase {
|
||||||
/**
|
/**
|
||||||
* Create a ppt with various fill effects
|
* Create a ppt with various fill effects
|
||||||
*/
|
*/
|
||||||
public void testBackgroundPicture() throws Exception {
|
@Test
|
||||||
|
public void backgroundPicture() throws Exception {
|
||||||
SlideShow ppt = new SlideShow();
|
SlideShow ppt = new SlideShow();
|
||||||
Slide slide;
|
Slide slide;
|
||||||
Fill fill;
|
Fill fill;
|
||||||
|
@ -191,8 +202,8 @@ public final class TestBackground extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getFillPictureRefCount(Shape shape, Fill fill) {
|
private int getFillPictureRefCount(Shape shape, Fill fill) {
|
||||||
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID);
|
EscherOptRecord opt = shape.getEscherOptRecord();
|
||||||
EscherSimpleProperty p = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
|
EscherSimpleProperty p = Shape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
|
||||||
if(p != null) {
|
if(p != null) {
|
||||||
int idx = p.getPropertyValue();
|
int idx = p.getPropertyValue();
|
||||||
|
|
||||||
|
@ -200,8 +211,8 @@ public final class TestBackground extends TestCase {
|
||||||
SlideShow ppt = sheet.getSlideShow();
|
SlideShow ppt = sheet.getSlideShow();
|
||||||
Document doc = ppt.getDocumentRecord();
|
Document doc = ppt.getDocumentRecord();
|
||||||
EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer();
|
EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer();
|
||||||
EscherContainerRecord bstore = (EscherContainerRecord)Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER);
|
EscherContainerRecord bstore = Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER);
|
||||||
List lst = bstore.getChildRecords();
|
List<EscherRecord> lst = bstore.getChildRecords();
|
||||||
return ((EscherBSERecord)lst.get(idx-1)).getRef();
|
return ((EscherBSERecord)lst.get(idx-1)).getRef();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -17,38 +17,60 @@
|
||||||
|
|
||||||
package org.apache.poi.hslf.model;
|
package org.apache.poi.hslf.model;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import static org.junit.Assert.assertEquals;
|
||||||
import org.apache.poi.hslf.usermodel.SlideShow;
|
import static org.junit.Assert.assertFalse;
|
||||||
import org.apache.poi.hslf.usermodel.RichTextRun;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import org.apache.poi.hslf.HSLFSlideShow;
|
import static org.junit.Assert.assertNull;
|
||||||
import org.apache.poi.ddf.*;
|
import static org.junit.Assert.assertTrue;
|
||||||
import org.apache.poi.POIDataSamples;
|
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
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
|
* Test drawing shapes via Graphics2D
|
||||||
*
|
*
|
||||||
* @author Yegor Kozlov
|
* @author Yegor Kozlov
|
||||||
*/
|
*/
|
||||||
public final class TestShapes extends TestCase {
|
public final class TestShapes {
|
||||||
private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
|
private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
|
||||||
|
|
||||||
private SlideShow ppt;
|
private SlideShow ppt;
|
||||||
private SlideShow pptB;
|
private SlideShow pptB;
|
||||||
|
|
||||||
protected void setUp() throws Exception {
|
@Before
|
||||||
ppt = new SlideShow(_slTests.openResourceAsStream("empty.ppt"));
|
public void setUp() throws Exception {
|
||||||
|
InputStream is1 = null, is2 = null;
|
||||||
pptB = new SlideShow(_slTests.openResourceAsStream("empty_textbox.ppt"));
|
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();
|
Slide slide = ppt.createSlide();
|
||||||
|
|
||||||
Line line = new Line();
|
Line line = new Line();
|
||||||
|
@ -92,7 +114,8 @@ public final class TestShapes extends TestCase {
|
||||||
* Verify that we can read TextBox shapes
|
* Verify that we can read TextBox shapes
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public void testTextBoxRead() throws Exception {
|
@Test
|
||||||
|
public void textBoxRead() throws Exception {
|
||||||
ppt = new SlideShow(_slTests.openResourceAsStream("with_textbox.ppt"));
|
ppt = new SlideShow(_slTests.openResourceAsStream("with_textbox.ppt"));
|
||||||
Slide sl = ppt.getSlides()[0];
|
Slide sl = ppt.getSlides()[0];
|
||||||
Shape[] sh = sl.getShapes();
|
Shape[] sh = sl.getShapes();
|
||||||
|
@ -127,7 +150,8 @@ public final class TestShapes extends TestCase {
|
||||||
* Verify that we can add TextBox shapes to a slide
|
* Verify that we can add TextBox shapes to a slide
|
||||||
* and set some of the style attributes
|
* and set some of the style attributes
|
||||||
*/
|
*/
|
||||||
public void testTextBoxWriteBytes() throws Exception {
|
@Test
|
||||||
|
public void textBoxWriteBytes() throws Exception {
|
||||||
ppt = new SlideShow();
|
ppt = new SlideShow();
|
||||||
Slide sl = ppt.createSlide();
|
Slide sl = ppt.createSlide();
|
||||||
RichTextRun rt;
|
RichTextRun rt;
|
||||||
|
@ -180,7 +204,8 @@ public final class TestShapes extends TestCase {
|
||||||
/**
|
/**
|
||||||
* Test with an empty text box
|
* Test with an empty text box
|
||||||
*/
|
*/
|
||||||
public void testEmptyTextBox() {
|
@Test
|
||||||
|
public void emptyTextBox() {
|
||||||
assertEquals(2, pptB.getSlides().length);
|
assertEquals(2, pptB.getSlides().length);
|
||||||
Slide s1 = pptB.getSlides()[0];
|
Slide s1 = pptB.getSlides()[0];
|
||||||
Slide s2 = pptB.getSlides()[1];
|
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
|
* 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().
|
* 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("with_textbox.ppt");
|
||||||
textBoxSet("basic_test_ppt_file.ppt");
|
textBoxSet("basic_test_ppt_file.ppt");
|
||||||
textBoxSet("next_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));
|
SlideShow ppt = new SlideShow(_slTests.openResourceAsStream(filename));
|
||||||
Slide[] sl = ppt.getSlides();
|
Slide[] sl = ppt.getSlides();
|
||||||
for (int k = 0; k < sl.length; k++) {
|
for (int k = 0; k < sl.length; k++) {
|
||||||
ArrayList lst1 = new ArrayList();
|
ArrayList<String> lst1 = new ArrayList<String>();
|
||||||
TextRun[] txt = sl[k].getTextRuns();
|
TextRun[] txt = sl[k].getTextRuns();
|
||||||
for (int i = 0; i < txt.length; i++) {
|
for (int i = 0; i < txt.length; i++) {
|
||||||
lst1.add(txt[i].getText());
|
lst1.add(txt[i].getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList lst2 = new ArrayList();
|
ArrayList<String> lst2 = new ArrayList<String>();
|
||||||
Shape[] sh = sl[k].getShapes();
|
Shape[] sh = sl[k].getShapes();
|
||||||
for (int i = 0; i < sh.length; i++) {
|
for (int i = 0; i < sh.length; i++) {
|
||||||
if (sh[i] instanceof TextShape){
|
if (sh[i] instanceof TextShape){
|
||||||
|
@ -229,7 +255,8 @@ public final class TestShapes extends TestCase {
|
||||||
/**
|
/**
|
||||||
* Test adding shapes to <code>ShapeGroup</code>
|
* Test adding shapes to <code>ShapeGroup</code>
|
||||||
*/
|
*/
|
||||||
public void testShapeGroup() throws Exception {
|
@Test
|
||||||
|
public void shapeGroup() throws Exception {
|
||||||
SlideShow ppt = new SlideShow();
|
SlideShow ppt = new SlideShow();
|
||||||
|
|
||||||
Slide slide = ppt.createSlide();
|
Slide slide = ppt.createSlide();
|
||||||
|
@ -280,7 +307,8 @@ public final class TestShapes extends TestCase {
|
||||||
/**
|
/**
|
||||||
* Test functionality of Sheet.removeShape(Shape shape)
|
* Test functionality of Sheet.removeShape(Shape shape)
|
||||||
*/
|
*/
|
||||||
public void testRemoveShapes() throws IOException {
|
@Test
|
||||||
|
public void removeShapes() throws IOException {
|
||||||
String file = "with_textbox.ppt";
|
String file = "with_textbox.ppt";
|
||||||
SlideShow ppt = new SlideShow(_slTests.openResourceAsStream(file));
|
SlideShow ppt = new SlideShow(_slTests.openResourceAsStream(file));
|
||||||
Slide sl = ppt.getSlides()[0];
|
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);
|
assertEquals("expected 0 shaped in " + file, 0, sl.getShapes().length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testLineWidth() {
|
@Test
|
||||||
|
public void lineWidth() {
|
||||||
SimpleShape sh = new AutoShape(ShapeTypes.RightTriangle);
|
SimpleShape sh = new AutoShape(ShapeTypes.RightTriangle);
|
||||||
|
|
||||||
EscherOptRecord opt = (EscherOptRecord)SimpleShape.getEscherChild(sh.getSpContainer(), EscherOptRecord.RECORD_ID);
|
EscherOptRecord opt = sh.getEscherOptRecord();
|
||||||
EscherSimpleProperty prop = (EscherSimpleProperty)SimpleShape.getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH);
|
EscherSimpleProperty prop = SimpleShape.getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH);
|
||||||
assertNull(prop);
|
assertNull(prop);
|
||||||
assertEquals(SimpleShape.DEFAULT_LINE_WIDTH, sh.getLineWidth());
|
assertEquals(SimpleShape.DEFAULT_LINE_WIDTH, sh.getLineWidth(), 0);
|
||||||
|
|
||||||
sh.setLineWidth(1.0);
|
sh.setLineWidth(1.0);
|
||||||
prop = (EscherSimpleProperty)SimpleShape.getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH);
|
prop = SimpleShape.getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH);
|
||||||
assertNotNull(prop);
|
assertNotNull(prop);
|
||||||
assertEquals(1.0, sh.getLineWidth());
|
assertEquals(1.0, sh.getLineWidth(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testShapeId() {
|
@Test
|
||||||
|
public void shapeId() {
|
||||||
SlideShow ppt = new SlideShow();
|
SlideShow ppt = new SlideShow();
|
||||||
Slide slide = ppt.createSlide();
|
Slide slide = ppt.createSlide();
|
||||||
Shape shape = null;
|
Shape shape = null;
|
||||||
|
@ -367,7 +397,8 @@ public final class TestShapes extends TestCase {
|
||||||
assertEquals(numClusters + 1, dgg.getNumIdClusters());
|
assertEquals(numClusters + 1, dgg.getNumIdClusters());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testLineColor() throws IOException {
|
@Test
|
||||||
|
public void lineColor() throws IOException {
|
||||||
SlideShow ppt = new SlideShow(_slTests.openResourceAsStream("51731.ppt"));
|
SlideShow ppt = new SlideShow(_slTests.openResourceAsStream("51731.ppt"));
|
||||||
Shape[] shape = ppt.getSlides()[0].getShapes();
|
Shape[] shape = ppt.getSlides()[0].getShapes();
|
||||||
|
|
||||||
|
@ -384,11 +415,11 @@ public final class TestShapes extends TestCase {
|
||||||
TextShape sh3 = (TextShape)shape[2];
|
TextShape sh3 = (TextShape)shape[2];
|
||||||
assertEquals("Text in a black border", sh3.getText());
|
assertEquals("Text in a black border", sh3.getText());
|
||||||
assertEquals(Color.black, sh3.getLineColor());
|
assertEquals(Color.black, sh3.getLineColor());
|
||||||
assertEquals(0.75, sh3.getLineWidth());
|
assertEquals(0.75, sh3.getLineWidth(), 0);
|
||||||
|
|
||||||
TextShape sh4 = (TextShape)shape[3];
|
TextShape sh4 = (TextShape)shape[3];
|
||||||
assertEquals("Border width is 5 pt", sh4.getText());
|
assertEquals("Border width is 5 pt", sh4.getText());
|
||||||
assertEquals(Color.black, sh4.getLineColor());
|
assertEquals(Color.black, sh4.getLineColor());
|
||||||
assertEquals(5.0, sh4.getLineWidth());
|
assertEquals(5.0, sh4.getLineWidth(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,14 @@ import java.util.Set;
|
||||||
import junit.framework.AssertionFailedError;
|
import junit.framework.AssertionFailedError;
|
||||||
|
|
||||||
import org.apache.poi.POIDataSamples;
|
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.HSLFSlideShow;
|
||||||
import org.apache.poi.hslf.HSLFTestDataSamples;
|
import org.apache.poi.hslf.HSLFTestDataSamples;
|
||||||
import org.apache.poi.hslf.exceptions.OldPowerPointFormatException;
|
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.Background;
|
||||||
import org.apache.poi.hslf.model.Fill;
|
import org.apache.poi.hslf.model.Fill;
|
||||||
import org.apache.poi.hslf.model.HeadersFooters;
|
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;
|
||||||
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
|
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
|
||||||
import org.apache.poi.hslf.record.TextHeaderAtom;
|
import org.apache.poi.hslf.record.TextHeaderAtom;
|
||||||
|
import org.apache.poi.util.LittleEndian;
|
||||||
import org.apache.poi.util.StringUtil;
|
import org.apache.poi.util.StringUtil;
|
||||||
|
import org.apache.poi.util.Units;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -625,4 +632,36 @@ public final class TestBugs {
|
||||||
inputStream.close();
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue