picture loading completely rewritten, bugs 51902 and 51890 fixed
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1177709 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f0a96deeb0
commit
7ecbfcfbaa
@ -34,6 +34,8 @@
|
|||||||
|
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.8-beta5" date="2011-??-??">
|
<release version="3.8-beta5" date="2011-??-??">
|
||||||
|
<action dev="poi-developers" type="fix">51902 - Picture.fillRawImageContent - ArrayIndexOutOfBoundsException (duplicate)</action>
|
||||||
|
<action dev="poi-developers" type="fix">51890 - ArrayIndexOutOfBounds ExceptionPicture.fillRawImageContent</action>
|
||||||
<action dev="poi-developers" type="add">Allow the passing of a File object to WorkbookFactory.create, which permits lower memory processing than the InputStream version</action>
|
<action dev="poi-developers" type="add">Allow the passing of a File object to WorkbookFactory.create, which permits lower memory processing than the InputStream version</action>
|
||||||
<action dev="poi-developers" type="fix">51873 - update HSMF to ignore Outlook 2002 Olk10SideProp entries, which don't behave like normal chunks</action>
|
<action dev="poi-developers" type="fix">51873 - update HSMF to ignore Outlook 2002 Olk10SideProp entries, which don't behave like normal chunks</action>
|
||||||
<action dev="poi-developers" type="fix">51850 - support creating comments in XSSF on an earlier slide when later ones already have them</action>
|
<action dev="poi-developers" type="fix">51850 - support creating comments in XSSF on an earlier slide when later ones already have them</action>
|
||||||
|
@ -420,11 +420,26 @@ public class LittleEndian implements LittleEndianConsts {
|
|||||||
* @param data the byte array.
|
* @param data the byte array.
|
||||||
* @param offset a starting offset into the byte array.
|
* @param offset a starting offset into the byte array.
|
||||||
* @return the unsigned value of the byte as a 32 bit integer
|
* @return the unsigned value of the byte as a 32 bit integer
|
||||||
|
* @deprecated Use {@link #getUByte(byte[], int)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static int getUnsignedByte(byte[] data, int offset) {
|
public static int getUnsignedByte(byte[] data, int offset) {
|
||||||
return data[offset] & 0xFF;
|
return data[offset] & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the unsigned value of a byte.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* the byte array.
|
||||||
|
* @param offset
|
||||||
|
* a starting offset into the byte array.
|
||||||
|
* @return the unsigned value of the byte as a 16 bit short
|
||||||
|
*/
|
||||||
|
public static short getUByte( byte[] data, int offset )
|
||||||
|
{
|
||||||
|
return (short) ( data[offset] & 0xFF );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy a portion of a byte array
|
* Copy a portion of a byte array
|
||||||
|
148
src/scratchpad/src/org/apache/poi/hwpf/model/PICF.java
Normal file
148
src/scratchpad/src/org/apache/poi/hwpf/model/PICF.java
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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.hwpf.model;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.apache.poi.hwpf.model.types.PICFAbstractType;
|
||||||
|
import org.apache.poi.util.Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PICF structure specifies the type of a picture, as well as the size of
|
||||||
|
* the picture and information about its border.
|
||||||
|
* <p>
|
||||||
|
* Class and fields descriptions are quoted from Microsoft Office Word 97-2007
|
||||||
|
* Binary File Format and [MS-DOC] - v20110608 Word (.doc) Binary File Format
|
||||||
|
*
|
||||||
|
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public class PICF extends PICFAbstractType
|
||||||
|
{
|
||||||
|
|
||||||
|
public PICF()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PICF( byte[] std, int offset )
|
||||||
|
{
|
||||||
|
fillFields( std, offset );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals( Object obj )
|
||||||
|
{
|
||||||
|
if ( this == obj )
|
||||||
|
return true;
|
||||||
|
if ( obj == null )
|
||||||
|
return false;
|
||||||
|
if ( getClass() != obj.getClass() )
|
||||||
|
return false;
|
||||||
|
PICF other = (PICF) obj;
|
||||||
|
if ( field_10_padding2 != other.field_10_padding2 )
|
||||||
|
return false;
|
||||||
|
if ( field_11_dxaGoal != other.field_11_dxaGoal )
|
||||||
|
return false;
|
||||||
|
if ( field_12_dyaGoal != other.field_12_dyaGoal )
|
||||||
|
return false;
|
||||||
|
if ( field_13_mx != other.field_13_mx )
|
||||||
|
return false;
|
||||||
|
if ( field_14_my != other.field_14_my )
|
||||||
|
return false;
|
||||||
|
if ( field_15_dxaReserved1 != other.field_15_dxaReserved1 )
|
||||||
|
return false;
|
||||||
|
if ( field_16_dyaReserved1 != other.field_16_dyaReserved1 )
|
||||||
|
return false;
|
||||||
|
if ( field_17_dxaReserved2 != other.field_17_dxaReserved2 )
|
||||||
|
return false;
|
||||||
|
if ( field_18_dyaReserved2 != other.field_18_dyaReserved2 )
|
||||||
|
return false;
|
||||||
|
if ( field_19_fReserved != other.field_19_fReserved )
|
||||||
|
return false;
|
||||||
|
if ( field_1_lcb != other.field_1_lcb )
|
||||||
|
return false;
|
||||||
|
if ( field_20_bpp != other.field_20_bpp )
|
||||||
|
return false;
|
||||||
|
if ( !Arrays.equals( field_21_brcTop80, other.field_21_brcTop80 ) )
|
||||||
|
return false;
|
||||||
|
if ( !Arrays.equals( field_22_brcLeft80, other.field_22_brcLeft80 ) )
|
||||||
|
return false;
|
||||||
|
if ( !Arrays.equals( field_23_brcBottom80, other.field_23_brcBottom80 ) )
|
||||||
|
return false;
|
||||||
|
if ( !Arrays.equals( field_24_brcRight80, other.field_24_brcRight80 ) )
|
||||||
|
return false;
|
||||||
|
if ( field_25_dxaReserved3 != other.field_25_dxaReserved3 )
|
||||||
|
return false;
|
||||||
|
if ( field_26_dyaReserved3 != other.field_26_dyaReserved3 )
|
||||||
|
return false;
|
||||||
|
if ( field_27_cProps != other.field_27_cProps )
|
||||||
|
return false;
|
||||||
|
if ( field_2_cbHeader != other.field_2_cbHeader )
|
||||||
|
return false;
|
||||||
|
if ( field_3_mm != other.field_3_mm )
|
||||||
|
return false;
|
||||||
|
if ( field_4_xExt != other.field_4_xExt )
|
||||||
|
return false;
|
||||||
|
if ( field_5_yExt != other.field_5_yExt )
|
||||||
|
return false;
|
||||||
|
if ( field_6_swHMF != other.field_6_swHMF )
|
||||||
|
return false;
|
||||||
|
if ( field_7_grf != other.field_7_grf )
|
||||||
|
return false;
|
||||||
|
if ( field_8_padding != other.field_8_padding )
|
||||||
|
return false;
|
||||||
|
if ( field_9_mmPM != other.field_9_mmPM )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + field_10_padding2;
|
||||||
|
result = prime * result + field_11_dxaGoal;
|
||||||
|
result = prime * result + field_12_dyaGoal;
|
||||||
|
result = prime * result + field_13_mx;
|
||||||
|
result = prime * result + field_14_my;
|
||||||
|
result = prime * result + field_15_dxaReserved1;
|
||||||
|
result = prime * result + field_16_dyaReserved1;
|
||||||
|
result = prime * result + field_17_dxaReserved2;
|
||||||
|
result = prime * result + field_18_dyaReserved2;
|
||||||
|
result = prime * result + field_19_fReserved;
|
||||||
|
result = prime * result + field_1_lcb;
|
||||||
|
result = prime * result + field_20_bpp;
|
||||||
|
result = prime * result + Arrays.hashCode( field_21_brcTop80 );
|
||||||
|
result = prime * result + Arrays.hashCode( field_22_brcLeft80 );
|
||||||
|
result = prime * result + Arrays.hashCode( field_23_brcBottom80 );
|
||||||
|
result = prime * result + Arrays.hashCode( field_24_brcRight80 );
|
||||||
|
result = prime * result + field_25_dxaReserved3;
|
||||||
|
result = prime * result + field_26_dyaReserved3;
|
||||||
|
result = prime * result + field_27_cProps;
|
||||||
|
result = prime * result + field_2_cbHeader;
|
||||||
|
result = prime * result + field_3_mm;
|
||||||
|
result = prime * result + field_4_xExt;
|
||||||
|
result = prime * result + field_5_yExt;
|
||||||
|
result = prime * result + field_6_swHMF;
|
||||||
|
result = prime * result + field_7_grf;
|
||||||
|
result = prime * result + field_8_padding;
|
||||||
|
result = prime * result + field_9_mmPM;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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.hwpf.model;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.poi.ddf.DefaultEscherRecordFactory;
|
||||||
|
import org.apache.poi.ddf.EscherContainerRecord;
|
||||||
|
import org.apache.poi.ddf.EscherRecord;
|
||||||
|
import org.apache.poi.util.Internal;
|
||||||
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public class PICFAndOfficeArtData
|
||||||
|
{
|
||||||
|
|
||||||
|
private List<EscherRecord> _blipRecords;
|
||||||
|
|
||||||
|
private short _cchPicName;
|
||||||
|
|
||||||
|
private PICF _picf;
|
||||||
|
|
||||||
|
private EscherContainerRecord _shape;
|
||||||
|
|
||||||
|
private byte[] _stPicName;
|
||||||
|
|
||||||
|
public PICFAndOfficeArtData( byte[] dataStream, int startOffset )
|
||||||
|
{
|
||||||
|
int offset = startOffset;
|
||||||
|
|
||||||
|
_picf = new PICF( dataStream, offset );
|
||||||
|
offset += PICF.getSize();
|
||||||
|
|
||||||
|
if ( _picf.getMm() == 0x0066 )
|
||||||
|
{
|
||||||
|
_cchPicName = LittleEndian.getUByte( dataStream, offset );
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
_stPicName = LittleEndian.getByteArray( dataStream, offset,
|
||||||
|
_cchPicName );
|
||||||
|
offset += _cchPicName;
|
||||||
|
}
|
||||||
|
|
||||||
|
final DefaultEscherRecordFactory escherRecordFactory = new DefaultEscherRecordFactory();
|
||||||
|
_shape = new EscherContainerRecord();
|
||||||
|
int recordSize = _shape.fillFields( dataStream, offset,
|
||||||
|
escherRecordFactory );
|
||||||
|
offset += recordSize;
|
||||||
|
|
||||||
|
_blipRecords = new LinkedList<EscherRecord>();
|
||||||
|
while ( ( offset - startOffset ) < _picf.getLcb() )
|
||||||
|
{
|
||||||
|
EscherRecord nextRecord = escherRecordFactory.createRecord(
|
||||||
|
dataStream, offset );
|
||||||
|
if ( nextRecord.getRecordId() != (short) 0xF007
|
||||||
|
&& ( nextRecord.getRecordId() < (short) 0xF018 || nextRecord
|
||||||
|
.getRecordId() > (short) 0xF117 ) )
|
||||||
|
break;
|
||||||
|
|
||||||
|
int blipRecordSize = nextRecord.fillFields( dataStream, offset,
|
||||||
|
escherRecordFactory );
|
||||||
|
offset += blipRecordSize;
|
||||||
|
|
||||||
|
_blipRecords.add( nextRecord );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<EscherRecord> getBlipRecords()
|
||||||
|
{
|
||||||
|
return _blipRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PICF getPicf()
|
||||||
|
{
|
||||||
|
return _picf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EscherContainerRecord getShape()
|
||||||
|
{
|
||||||
|
return _shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getStPicName()
|
||||||
|
{
|
||||||
|
return _stPicName;
|
||||||
|
}
|
||||||
|
}
|
@ -1,221 +0,0 @@
|
|||||||
/* ====================================================================
|
|
||||||
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.hwpf.model;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.apache.poi.util.Internal;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Picture Descriptor (on File) (PICF)
|
|
||||||
* <p>
|
|
||||||
* Based on Microsoft Office Word 97-2007 Binary File Format (.doc)
|
|
||||||
* Specification; Page 181 of 210
|
|
||||||
*
|
|
||||||
* @author Sergey Vladimirov ( vlsergey {at} gmail {dot} com )
|
|
||||||
*/
|
|
||||||
@Internal
|
|
||||||
public class PictureDescriptor
|
|
||||||
{
|
|
||||||
private static final int LCB_OFFSET = 0x00;
|
|
||||||
private static final int CBHEADER_OFFSET = 0x04;
|
|
||||||
|
|
||||||
private static final int MFP_MM_OFFSET = 0x06;
|
|
||||||
private static final int MFP_XEXT_OFFSET = 0x08;
|
|
||||||
private static final int MFP_YEXT_OFFSET = 0x0A;
|
|
||||||
private static final int MFP_HMF_OFFSET = 0x0C;
|
|
||||||
|
|
||||||
private static final int DXAGOAL_OFFSET = 0x1C;
|
|
||||||
private static final int DYAGOAL_OFFSET = 0x1E;
|
|
||||||
|
|
||||||
private static final int MX_OFFSET = 0x20;
|
|
||||||
private static final int MY_OFFSET = 0x22;
|
|
||||||
|
|
||||||
private static final int DXACROPLEFT_OFFSET = 0x24;
|
|
||||||
private static final int DYACROPTOP_OFFSET = 0x26;
|
|
||||||
private static final int DXACROPRIGHT_OFFSET = 0x28;
|
|
||||||
private static final int DYACROPBOTTOM_OFFSET = 0x2A;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of bytes in the PIC structure plus size of following picture data
|
|
||||||
* which may be a Window's metafile, a bitmap, or the filename of a TIFF
|
|
||||||
* file. In the case of a Macintosh PICT picture, this includes the size of
|
|
||||||
* the PIC, the standard "x" metafile, and the Macintosh PICT data. See
|
|
||||||
* Appendix B for more information.
|
|
||||||
*/
|
|
||||||
protected int lcb;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of bytes in the PIC (to allow for future expansion).
|
|
||||||
*/
|
|
||||||
protected int cbHeader;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Microsoft Office Word 97-2007 Binary File Format (.doc) Specification
|
|
||||||
*
|
|
||||||
* Page 181 of 210
|
|
||||||
*
|
|
||||||
* If a Windows metafile is stored immediately following the PIC structure,
|
|
||||||
* the mfp is a Window's METAFILEPICT structure. See
|
|
||||||
* http://msdn2.microsoft.com/en-us/library/ms649017(VS.85).aspx for more
|
|
||||||
* information about the METAFILEPICT structure and
|
|
||||||
* http://download.microsoft.com/download/0/B/E/0BE8BDD7-E5E8-422A-ABFD-
|
|
||||||
* 4342ED7AD886/WindowsMetafileFormat(wmf)Specification.pdf for Windows
|
|
||||||
* Metafile Format specification.
|
|
||||||
*
|
|
||||||
* When the data immediately following the PIC is a TIFF filename,
|
|
||||||
* mfp.mm==98 If a bitmap is stored after the pic, mfp.mm==99.
|
|
||||||
*
|
|
||||||
* When the PIC describes a bitmap, mfp.xExt is the width of the bitmap in
|
|
||||||
* pixels and mfp.yExt is the height of the bitmap in pixels.
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected int mfp_mm;
|
|
||||||
protected int mfp_xExt;
|
|
||||||
protected int mfp_yExt;
|
|
||||||
protected int mfp_hMF;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <li>Window's bitmap structure when PIC describes a BITMAP (14 bytes)
|
|
||||||
*
|
|
||||||
* <li>Rectangle for window origin and extents when metafile is stored --
|
|
||||||
* ignored if 0 (8 bytes)
|
|
||||||
*/
|
|
||||||
protected byte[] offset14 = new byte[14];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Horizontal measurement in twips of the rectangle the picture should be
|
|
||||||
* imaged within
|
|
||||||
*/
|
|
||||||
protected short dxaGoal = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vertical measurement in twips of the rectangle the picture should be
|
|
||||||
* imaged within
|
|
||||||
*/
|
|
||||||
protected short dyaGoal = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Horizontal scaling factor supplied by user expressed in .001% units
|
|
||||||
*/
|
|
||||||
protected short mx;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vertical scaling factor supplied by user expressed in .001% units
|
|
||||||
*/
|
|
||||||
protected short my;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount the picture has been cropped on the left in twips
|
|
||||||
*/
|
|
||||||
protected short dxaCropLeft = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount the picture has been cropped on the top in twips
|
|
||||||
*/
|
|
||||||
protected short dyaCropTop = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount the picture has been cropped on the right in twips
|
|
||||||
*/
|
|
||||||
protected short dxaCropRight = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount the picture has been cropped on the bottom in twips
|
|
||||||
*/
|
|
||||||
protected short dyaCropBottom = 0;
|
|
||||||
|
|
||||||
public PictureDescriptor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public PictureDescriptor( byte[] _dataStream, int startOffset )
|
|
||||||
{
|
|
||||||
this.lcb = LittleEndian.getInt( _dataStream, startOffset + LCB_OFFSET );
|
|
||||||
this.cbHeader = LittleEndian.getUShort( _dataStream, startOffset
|
|
||||||
+ CBHEADER_OFFSET );
|
|
||||||
|
|
||||||
this.mfp_mm = LittleEndian.getUShort( _dataStream, startOffset
|
|
||||||
+ MFP_MM_OFFSET );
|
|
||||||
this.mfp_xExt = LittleEndian.getUShort( _dataStream, startOffset
|
|
||||||
+ MFP_XEXT_OFFSET );
|
|
||||||
this.mfp_yExt = LittleEndian.getUShort( _dataStream, startOffset
|
|
||||||
+ MFP_YEXT_OFFSET );
|
|
||||||
this.mfp_hMF = LittleEndian.getUShort( _dataStream, startOffset
|
|
||||||
+ MFP_HMF_OFFSET );
|
|
||||||
|
|
||||||
this.offset14 = LittleEndian.getByteArray( _dataStream,
|
|
||||||
startOffset + 0x0E, 14 );
|
|
||||||
|
|
||||||
this.dxaGoal = LittleEndian.getShort( _dataStream, startOffset
|
|
||||||
+ DXAGOAL_OFFSET );
|
|
||||||
this.dyaGoal = LittleEndian.getShort( _dataStream, startOffset
|
|
||||||
+ DYAGOAL_OFFSET );
|
|
||||||
|
|
||||||
this.mx = LittleEndian.getShort( _dataStream, startOffset + MX_OFFSET );
|
|
||||||
this.my = LittleEndian.getShort( _dataStream, startOffset + MY_OFFSET );
|
|
||||||
|
|
||||||
this.dxaCropLeft = LittleEndian.getShort( _dataStream, startOffset
|
|
||||||
+ DXACROPLEFT_OFFSET );
|
|
||||||
this.dyaCropTop = LittleEndian.getShort( _dataStream, startOffset
|
|
||||||
+ DYACROPTOP_OFFSET );
|
|
||||||
this.dxaCropRight = LittleEndian.getShort( _dataStream, startOffset
|
|
||||||
+ DXACROPRIGHT_OFFSET );
|
|
||||||
this.dyaCropBottom = LittleEndian.getShort( _dataStream, startOffset
|
|
||||||
+ DYACROPBOTTOM_OFFSET );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
stringBuilder.append( "[PICF]\n" );
|
|
||||||
stringBuilder.append( " lcb = " ).append( this.lcb )
|
|
||||||
.append( '\n' );
|
|
||||||
stringBuilder.append( " cbHeader = " )
|
|
||||||
.append( this.cbHeader ).append( '\n' );
|
|
||||||
|
|
||||||
stringBuilder.append( " mfp.mm = " ).append( this.mfp_mm )
|
|
||||||
.append( '\n' );
|
|
||||||
stringBuilder.append( " mfp.xExt = " )
|
|
||||||
.append( this.mfp_xExt ).append( '\n' );
|
|
||||||
stringBuilder.append( " mfp.yExt = " )
|
|
||||||
.append( this.mfp_yExt ).append( '\n' );
|
|
||||||
stringBuilder.append( " mfp.hMF = " )
|
|
||||||
.append( this.mfp_hMF ).append( '\n' );
|
|
||||||
|
|
||||||
stringBuilder.append( " offset14 = " )
|
|
||||||
.append( Arrays.toString( this.offset14 ) ).append( '\n' );
|
|
||||||
stringBuilder.append( " dxaGoal = " )
|
|
||||||
.append( this.dxaGoal ).append( '\n' );
|
|
||||||
stringBuilder.append( " dyaGoal = " )
|
|
||||||
.append( this.dyaGoal ).append( '\n' );
|
|
||||||
|
|
||||||
stringBuilder.append( " dxaCropLeft = " )
|
|
||||||
.append( this.dxaCropLeft ).append( '\n' );
|
|
||||||
stringBuilder.append( " dyaCropTop = " )
|
|
||||||
.append( this.dyaCropTop ).append( '\n' );
|
|
||||||
stringBuilder.append( " dxaCropRight = " )
|
|
||||||
.append( this.dxaCropRight ).append( '\n' );
|
|
||||||
stringBuilder.append( " dyaCropBottom = " )
|
|
||||||
.append( this.dyaCropBottom ).append( '\n' );
|
|
||||||
|
|
||||||
stringBuilder.append( "[/PICF]" );
|
|
||||||
return stringBuilder.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,692 @@
|
|||||||
|
|
||||||
|
package org.apache.poi.hwpf.model.types;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.poi.hwpf.usermodel.*;
|
||||||
|
import org.apache.poi.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PICF structure specifies the type of a picture, as well as the size of the
|
||||||
|
picture and information about its border. <p>Class and fields descriptions are quoted
|
||||||
|
from Microsoft Office Word 97-2007
|
||||||
|
Binary File Format and [MS-DOC] - v20110608 Word (.doc)
|
||||||
|
Binary File Format
|
||||||
|
|
||||||
|
* <p>
|
||||||
|
* NOTE: This source is automatically generated please do not modify this file. Either subclass or
|
||||||
|
* remove the record in src/types/definitions.
|
||||||
|
* <p>
|
||||||
|
* This class is internal. It content or properties may change without notice
|
||||||
|
* due to changes in our knowledge of internal Microsoft Word binary structures.
|
||||||
|
|
||||||
|
* @author Sergey Vladimirov; according to Microsoft Office Word 97-2007 Binary File Format
|
||||||
|
Specification [*.doc] and [MS-DOC] - v20110608 Word (.doc) Binary File Format
|
||||||
|
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public abstract class PICFAbstractType
|
||||||
|
{
|
||||||
|
|
||||||
|
protected int field_1_lcb;
|
||||||
|
protected int field_2_cbHeader;
|
||||||
|
protected short field_3_mm;
|
||||||
|
protected short field_4_xExt;
|
||||||
|
protected short field_5_yExt;
|
||||||
|
protected short field_6_swHMF;
|
||||||
|
protected int field_7_grf;
|
||||||
|
protected int field_8_padding;
|
||||||
|
protected int field_9_mmPM;
|
||||||
|
protected int field_10_padding2;
|
||||||
|
protected short field_11_dxaGoal;
|
||||||
|
protected short field_12_dyaGoal;
|
||||||
|
protected int field_13_mx;
|
||||||
|
protected int field_14_my;
|
||||||
|
protected short field_15_dxaReserved1;
|
||||||
|
protected short field_16_dyaReserved1;
|
||||||
|
protected short field_17_dxaReserved2;
|
||||||
|
protected short field_18_dyaReserved2;
|
||||||
|
protected byte field_19_fReserved;
|
||||||
|
protected byte field_20_bpp;
|
||||||
|
protected byte[] field_21_brcTop80;
|
||||||
|
protected byte[] field_22_brcLeft80;
|
||||||
|
protected byte[] field_23_brcBottom80;
|
||||||
|
protected byte[] field_24_brcRight80;
|
||||||
|
protected short field_25_dxaReserved3;
|
||||||
|
protected short field_26_dyaReserved3;
|
||||||
|
protected short field_27_cProps;
|
||||||
|
|
||||||
|
protected PICFAbstractType()
|
||||||
|
{
|
||||||
|
this.field_21_brcTop80 = new byte[4];
|
||||||
|
this.field_22_brcLeft80 = new byte[4];
|
||||||
|
this.field_23_brcBottom80 = new byte[4];
|
||||||
|
this.field_24_brcRight80 = new byte[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fillFields( byte[] data, int offset )
|
||||||
|
{
|
||||||
|
field_1_lcb = LittleEndian.getInt( data, 0x0 + offset );
|
||||||
|
field_2_cbHeader = LittleEndian.getShort( data, 0x4 + offset );
|
||||||
|
field_3_mm = LittleEndian.getShort( data, 0x6 + offset );
|
||||||
|
field_4_xExt = LittleEndian.getShort( data, 0x8 + offset );
|
||||||
|
field_5_yExt = LittleEndian.getShort( data, 0xa + offset );
|
||||||
|
field_6_swHMF = LittleEndian.getShort( data, 0xc + offset );
|
||||||
|
field_7_grf = LittleEndian.getInt( data, 0xe + offset );
|
||||||
|
field_8_padding = LittleEndian.getInt( data, 0x12 + offset );
|
||||||
|
field_9_mmPM = LittleEndian.getShort( data, 0x16 + offset );
|
||||||
|
field_10_padding2 = LittleEndian.getInt( data, 0x18 + offset );
|
||||||
|
field_11_dxaGoal = LittleEndian.getShort( data, 0x1c + offset );
|
||||||
|
field_12_dyaGoal = LittleEndian.getShort( data, 0x1e + offset );
|
||||||
|
field_13_mx = LittleEndian.getShort( data, 0x20 + offset );
|
||||||
|
field_14_my = LittleEndian.getShort( data, 0x22 + offset );
|
||||||
|
field_15_dxaReserved1 = LittleEndian.getShort( data, 0x24 + offset );
|
||||||
|
field_16_dyaReserved1 = LittleEndian.getShort( data, 0x26 + offset );
|
||||||
|
field_17_dxaReserved2 = LittleEndian.getShort( data, 0x28 + offset );
|
||||||
|
field_18_dyaReserved2 = LittleEndian.getShort( data, 0x2a + offset );
|
||||||
|
field_19_fReserved = data[ 0x2c + offset ];
|
||||||
|
field_20_bpp = data[ 0x2d + offset ];
|
||||||
|
field_21_brcTop80 = LittleEndian.getByteArray( data, 0x2e + offset,4 );
|
||||||
|
field_22_brcLeft80 = LittleEndian.getByteArray( data, 0x32 + offset,4 );
|
||||||
|
field_23_brcBottom80 = LittleEndian.getByteArray( data, 0x36 + offset,4 );
|
||||||
|
field_24_brcRight80 = LittleEndian.getByteArray( data, 0x3a + offset,4 );
|
||||||
|
field_25_dxaReserved3 = LittleEndian.getShort( data, 0x3e + offset );
|
||||||
|
field_26_dyaReserved3 = LittleEndian.getShort( data, 0x40 + offset );
|
||||||
|
field_27_cProps = LittleEndian.getShort( data, 0x42 + offset );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serialize( byte[] data, int offset )
|
||||||
|
{
|
||||||
|
LittleEndian.putInt( data, 0x0 + offset, field_1_lcb );
|
||||||
|
LittleEndian.putUShort( data, 0x4 + offset, field_2_cbHeader );
|
||||||
|
LittleEndian.putShort( data, 0x6 + offset, field_3_mm );
|
||||||
|
LittleEndian.putShort( data, 0x8 + offset, field_4_xExt );
|
||||||
|
LittleEndian.putShort( data, 0xa + offset, field_5_yExt );
|
||||||
|
LittleEndian.putShort( data, 0xc + offset, field_6_swHMF );
|
||||||
|
LittleEndian.putInt( data, 0xe + offset, field_7_grf );
|
||||||
|
LittleEndian.putInt( data, 0x12 + offset, field_8_padding );
|
||||||
|
LittleEndian.putUShort( data, 0x16 + offset, field_9_mmPM );
|
||||||
|
LittleEndian.putInt( data, 0x18 + offset, field_10_padding2 );
|
||||||
|
LittleEndian.putShort( data, 0x1c + offset, field_11_dxaGoal );
|
||||||
|
LittleEndian.putShort( data, 0x1e + offset, field_12_dyaGoal );
|
||||||
|
LittleEndian.putUShort( data, 0x20 + offset, field_13_mx );
|
||||||
|
LittleEndian.putUShort( data, 0x22 + offset, field_14_my );
|
||||||
|
LittleEndian.putShort( data, 0x24 + offset, field_15_dxaReserved1 );
|
||||||
|
LittleEndian.putShort( data, 0x26 + offset, field_16_dyaReserved1 );
|
||||||
|
LittleEndian.putShort( data, 0x28 + offset, field_17_dxaReserved2 );
|
||||||
|
LittleEndian.putShort( data, 0x2a + offset, field_18_dyaReserved2 );
|
||||||
|
data[ 0x2c + offset ] = field_19_fReserved;
|
||||||
|
data[ 0x2d + offset ] = field_20_bpp;
|
||||||
|
System.arraycopy( field_21_brcTop80, 0, data, 0x2e + offset, field_21_brcTop80.length );
|
||||||
|
System.arraycopy( field_22_brcLeft80, 0, data, 0x32 + offset, field_22_brcLeft80.length );
|
||||||
|
System.arraycopy( field_23_brcBottom80, 0, data, 0x36 + offset, field_23_brcBottom80.length );
|
||||||
|
System.arraycopy( field_24_brcRight80, 0, data, 0x3a + offset, field_24_brcRight80.length );
|
||||||
|
LittleEndian.putShort( data, 0x3e + offset, field_25_dxaReserved3 );
|
||||||
|
LittleEndian.putShort( data, 0x40 + offset, field_26_dyaReserved3 );
|
||||||
|
LittleEndian.putShort( data, 0x42 + offset, field_27_cProps );
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] serialize()
|
||||||
|
{
|
||||||
|
final byte[] result = new byte[ getSize() ];
|
||||||
|
serialize( result, 0 );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size of record
|
||||||
|
*/
|
||||||
|
public static int getSize()
|
||||||
|
{
|
||||||
|
return 0 + 4 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + 2 + 4 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 1 + 1 + 4 + 4 + 4 + 4 + 2 + 2 + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("[PICF]\n");
|
||||||
|
builder.append(" .lcb = ");
|
||||||
|
builder.append(" (").append(getLcb()).append(" )\n");
|
||||||
|
builder.append(" .cbHeader = ");
|
||||||
|
builder.append(" (").append(getCbHeader()).append(" )\n");
|
||||||
|
builder.append(" .mm = ");
|
||||||
|
builder.append(" (").append(getMm()).append(" )\n");
|
||||||
|
builder.append(" .xExt = ");
|
||||||
|
builder.append(" (").append(getXExt()).append(" )\n");
|
||||||
|
builder.append(" .yExt = ");
|
||||||
|
builder.append(" (").append(getYExt()).append(" )\n");
|
||||||
|
builder.append(" .swHMF = ");
|
||||||
|
builder.append(" (").append(getSwHMF()).append(" )\n");
|
||||||
|
builder.append(" .grf = ");
|
||||||
|
builder.append(" (").append(getGrf()).append(" )\n");
|
||||||
|
builder.append(" .padding = ");
|
||||||
|
builder.append(" (").append(getPadding()).append(" )\n");
|
||||||
|
builder.append(" .mmPM = ");
|
||||||
|
builder.append(" (").append(getMmPM()).append(" )\n");
|
||||||
|
builder.append(" .padding2 = ");
|
||||||
|
builder.append(" (").append(getPadding2()).append(" )\n");
|
||||||
|
builder.append(" .dxaGoal = ");
|
||||||
|
builder.append(" (").append(getDxaGoal()).append(" )\n");
|
||||||
|
builder.append(" .dyaGoal = ");
|
||||||
|
builder.append(" (").append(getDyaGoal()).append(" )\n");
|
||||||
|
builder.append(" .mx = ");
|
||||||
|
builder.append(" (").append(getMx()).append(" )\n");
|
||||||
|
builder.append(" .my = ");
|
||||||
|
builder.append(" (").append(getMy()).append(" )\n");
|
||||||
|
builder.append(" .dxaReserved1 = ");
|
||||||
|
builder.append(" (").append(getDxaReserved1()).append(" )\n");
|
||||||
|
builder.append(" .dyaReserved1 = ");
|
||||||
|
builder.append(" (").append(getDyaReserved1()).append(" )\n");
|
||||||
|
builder.append(" .dxaReserved2 = ");
|
||||||
|
builder.append(" (").append(getDxaReserved2()).append(" )\n");
|
||||||
|
builder.append(" .dyaReserved2 = ");
|
||||||
|
builder.append(" (").append(getDyaReserved2()).append(" )\n");
|
||||||
|
builder.append(" .fReserved = ");
|
||||||
|
builder.append(" (").append(getFReserved()).append(" )\n");
|
||||||
|
builder.append(" .bpp = ");
|
||||||
|
builder.append(" (").append(getBpp()).append(" )\n");
|
||||||
|
builder.append(" .brcTop80 = ");
|
||||||
|
builder.append(" (").append(getBrcTop80()).append(" )\n");
|
||||||
|
builder.append(" .brcLeft80 = ");
|
||||||
|
builder.append(" (").append(getBrcLeft80()).append(" )\n");
|
||||||
|
builder.append(" .brcBottom80 = ");
|
||||||
|
builder.append(" (").append(getBrcBottom80()).append(" )\n");
|
||||||
|
builder.append(" .brcRight80 = ");
|
||||||
|
builder.append(" (").append(getBrcRight80()).append(" )\n");
|
||||||
|
builder.append(" .dxaReserved3 = ");
|
||||||
|
builder.append(" (").append(getDxaReserved3()).append(" )\n");
|
||||||
|
builder.append(" .dyaReserved3 = ");
|
||||||
|
builder.append(" (").append(getDyaReserved3()).append(" )\n");
|
||||||
|
builder.append(" .cProps = ");
|
||||||
|
builder.append(" (").append(getCProps()).append(" )\n");
|
||||||
|
|
||||||
|
builder.append("[/PICF]\n");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A signed integer that specifies the size, in bytes, of this PICF structure and the subsequent data.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public int getLcb()
|
||||||
|
{
|
||||||
|
return field_1_lcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A signed integer that specifies the size, in bytes, of this PICF structure and the subsequent data.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setLcb( int field_1_lcb )
|
||||||
|
{
|
||||||
|
this.field_1_lcb = field_1_lcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An unsigned integer that specifies the size, in bytes, of this PICF structure. This value MUST be 0x44.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public int getCbHeader()
|
||||||
|
{
|
||||||
|
return field_2_cbHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An unsigned integer that specifies the size, in bytes, of this PICF structure. This value MUST be 0x44.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setCbHeader( int field_2_cbHeader )
|
||||||
|
{
|
||||||
|
this.field_2_cbHeader = field_2_cbHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A signed integer that specifies the format of the picture data.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getMm()
|
||||||
|
{
|
||||||
|
return field_3_mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A signed integer that specifies the format of the picture data.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setMm( short field_3_mm )
|
||||||
|
{
|
||||||
|
this.field_3_mm = field_3_mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field is unused and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getXExt()
|
||||||
|
{
|
||||||
|
return field_4_xExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field is unused and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setXExt( short field_4_xExt )
|
||||||
|
{
|
||||||
|
this.field_4_xExt = field_4_xExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field is unused and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getYExt()
|
||||||
|
{
|
||||||
|
return field_5_yExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field is unused and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setYExt( short field_5_yExt )
|
||||||
|
{
|
||||||
|
this.field_5_yExt = field_5_yExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field is unused and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getSwHMF()
|
||||||
|
{
|
||||||
|
return field_6_swHMF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field is unused and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setSwHMF( short field_6_swHMF )
|
||||||
|
{
|
||||||
|
this.field_6_swHMF = field_6_swHMF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public int getGrf()
|
||||||
|
{
|
||||||
|
return field_7_grf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setGrf( int field_7_grf )
|
||||||
|
{
|
||||||
|
this.field_7_grf = field_7_grf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This value MUST be zero and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public int getPadding()
|
||||||
|
{
|
||||||
|
return field_8_padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This value MUST be zero and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setPadding( int field_8_padding )
|
||||||
|
{
|
||||||
|
this.field_8_padding = field_8_padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public int getMmPM()
|
||||||
|
{
|
||||||
|
return field_9_mmPM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setMmPM( int field_9_mmPM )
|
||||||
|
{
|
||||||
|
this.field_9_mmPM = field_9_mmPM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This value MUST be zero and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public int getPadding2()
|
||||||
|
{
|
||||||
|
return field_10_padding2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This value MUST be zero and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setPadding2( int field_10_padding2 )
|
||||||
|
{
|
||||||
|
this.field_10_padding2 = field_10_padding2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dxaGoal field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getDxaGoal()
|
||||||
|
{
|
||||||
|
return field_11_dxaGoal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the dxaGoal field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setDxaGoal( short field_11_dxaGoal )
|
||||||
|
{
|
||||||
|
this.field_11_dxaGoal = field_11_dxaGoal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dyaGoal field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getDyaGoal()
|
||||||
|
{
|
||||||
|
return field_12_dyaGoal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the dyaGoal field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setDyaGoal( short field_12_dyaGoal )
|
||||||
|
{
|
||||||
|
this.field_12_dyaGoal = field_12_dyaGoal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the mx field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public int getMx()
|
||||||
|
{
|
||||||
|
return field_13_mx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the mx field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setMx( int field_13_mx )
|
||||||
|
{
|
||||||
|
this.field_13_mx = field_13_mx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the my field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public int getMy()
|
||||||
|
{
|
||||||
|
return field_14_my;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the my field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setMy( int field_14_my )
|
||||||
|
{
|
||||||
|
this.field_14_my = field_14_my;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dxaReserved1 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getDxaReserved1()
|
||||||
|
{
|
||||||
|
return field_15_dxaReserved1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the dxaReserved1 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setDxaReserved1( short field_15_dxaReserved1 )
|
||||||
|
{
|
||||||
|
this.field_15_dxaReserved1 = field_15_dxaReserved1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dyaReserved1 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getDyaReserved1()
|
||||||
|
{
|
||||||
|
return field_16_dyaReserved1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the dyaReserved1 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setDyaReserved1( short field_16_dyaReserved1 )
|
||||||
|
{
|
||||||
|
this.field_16_dyaReserved1 = field_16_dyaReserved1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dxaReserved2 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getDxaReserved2()
|
||||||
|
{
|
||||||
|
return field_17_dxaReserved2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the dxaReserved2 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setDxaReserved2( short field_17_dxaReserved2 )
|
||||||
|
{
|
||||||
|
this.field_17_dxaReserved2 = field_17_dxaReserved2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dyaReserved2 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getDyaReserved2()
|
||||||
|
{
|
||||||
|
return field_18_dyaReserved2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the dyaReserved2 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setDyaReserved2( short field_18_dyaReserved2 )
|
||||||
|
{
|
||||||
|
this.field_18_dyaReserved2 = field_18_dyaReserved2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the fReserved field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public byte getFReserved()
|
||||||
|
{
|
||||||
|
return field_19_fReserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the fReserved field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setFReserved( byte field_19_fReserved )
|
||||||
|
{
|
||||||
|
this.field_19_fReserved = field_19_fReserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bpp field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public byte getBpp()
|
||||||
|
{
|
||||||
|
return field_20_bpp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the bpp field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setBpp( byte field_20_bpp )
|
||||||
|
{
|
||||||
|
this.field_20_bpp = field_20_bpp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the brcTop80 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public byte[] getBrcTop80()
|
||||||
|
{
|
||||||
|
return field_21_brcTop80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the brcTop80 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setBrcTop80( byte[] field_21_brcTop80 )
|
||||||
|
{
|
||||||
|
this.field_21_brcTop80 = field_21_brcTop80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the brcLeft80 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public byte[] getBrcLeft80()
|
||||||
|
{
|
||||||
|
return field_22_brcLeft80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the brcLeft80 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setBrcLeft80( byte[] field_22_brcLeft80 )
|
||||||
|
{
|
||||||
|
this.field_22_brcLeft80 = field_22_brcLeft80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the brcBottom80 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public byte[] getBrcBottom80()
|
||||||
|
{
|
||||||
|
return field_23_brcBottom80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the brcBottom80 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setBrcBottom80( byte[] field_23_brcBottom80 )
|
||||||
|
{
|
||||||
|
this.field_23_brcBottom80 = field_23_brcBottom80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the brcRight80 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public byte[] getBrcRight80()
|
||||||
|
{
|
||||||
|
return field_24_brcRight80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the brcRight80 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setBrcRight80( byte[] field_24_brcRight80 )
|
||||||
|
{
|
||||||
|
this.field_24_brcRight80 = field_24_brcRight80;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dxaReserved3 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getDxaReserved3()
|
||||||
|
{
|
||||||
|
return field_25_dxaReserved3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the dxaReserved3 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setDxaReserved3( short field_25_dxaReserved3 )
|
||||||
|
{
|
||||||
|
this.field_25_dxaReserved3 = field_25_dxaReserved3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dyaReserved3 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getDyaReserved3()
|
||||||
|
{
|
||||||
|
return field_26_dyaReserved3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the dyaReserved3 field for the PICF record.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setDyaReserved3( short field_26_dyaReserved3 )
|
||||||
|
{
|
||||||
|
this.field_26_dyaReserved3 = field_26_dyaReserved3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This value MUST be 0 and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public short getCProps()
|
||||||
|
{
|
||||||
|
return field_27_cProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This value MUST be 0 and MUST be ignored.
|
||||||
|
*/
|
||||||
|
@Internal
|
||||||
|
public void setCProps( short field_27_cProps )
|
||||||
|
{
|
||||||
|
this.field_27_cProps = field_27_cProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // END OF CLASS
|
@ -23,8 +23,19 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.zip.InflaterInputStream;
|
import java.util.zip.InflaterInputStream;
|
||||||
|
|
||||||
import org.apache.poi.hwpf.model.PictureDescriptor;
|
import org.apache.poi.ddf.EscherSimpleProperty;
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
|
import org.apache.poi.ddf.EscherProperty;
|
||||||
|
|
||||||
|
import org.apache.poi.ddf.EscherOptRecord;
|
||||||
|
|
||||||
|
import org.apache.poi.ddf.EscherContainerRecord;
|
||||||
|
|
||||||
|
import org.apache.poi.ddf.EscherBSERecord;
|
||||||
|
import org.apache.poi.ddf.EscherBlipRecord;
|
||||||
|
import org.apache.poi.ddf.EscherRecord;
|
||||||
|
import org.apache.poi.hwpf.model.PICF;
|
||||||
|
import org.apache.poi.hwpf.model.PICFAndOfficeArtData;
|
||||||
import org.apache.poi.util.POILogFactory;
|
import org.apache.poi.util.POILogFactory;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
@ -33,91 +44,229 @@ import org.apache.poi.util.POILogger;
|
|||||||
*
|
*
|
||||||
* @author Dmitry Romanov
|
* @author Dmitry Romanov
|
||||||
*/
|
*/
|
||||||
public final class Picture extends PictureDescriptor
|
public final class Picture
|
||||||
{
|
{
|
||||||
private static final POILogger log = POILogFactory
|
|
||||||
.getLogger( Picture.class );
|
|
||||||
|
|
||||||
// public static final int FILENAME_OFFSET = 0x7C;
|
|
||||||
// public static final int FILENAME_SIZE_OFFSET = 0x6C;
|
|
||||||
static final int PICF_OFFSET = 0x0;
|
|
||||||
static final int PICT_HEADER_OFFSET = 0x4;
|
|
||||||
static final int MFPMM_OFFSET = 0x6;
|
|
||||||
static final int PICF_SHAPE_OFFSET = 0xE;
|
|
||||||
static final int UNKNOWN_HEADER_SIZE = 0x49;
|
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static final byte[] GIF = PictureType.GIF.getSignatures()[0];
|
public static final byte[] BMP = new byte[] { 'B', 'M' };
|
||||||
@Deprecated
|
|
||||||
public static final byte[] PNG = PictureType.PNG.getSignatures()[0];
|
|
||||||
@Deprecated
|
|
||||||
public static final byte[] JPG = PictureType.JPEG.getSignatures()[0];
|
|
||||||
@Deprecated
|
|
||||||
public static final byte[] BMP = PictureType.BMP.getSignatures()[0];
|
|
||||||
@Deprecated
|
|
||||||
public static final byte[] TIFF = PictureType.TIFF.getSignatures()[0];
|
|
||||||
@Deprecated
|
|
||||||
public static final byte[] TIFF1 = PictureType.TIFF.getSignatures()[1];
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public static final byte[] EMF = PictureType.EMF.getSignatures()[0];
|
|
||||||
@Deprecated
|
|
||||||
public static final byte[] WMF1 = PictureType.WMF.getSignatures()[0];
|
|
||||||
// Windows 3.x
|
|
||||||
@Deprecated
|
|
||||||
public static final byte[] WMF2 = PictureType.WMF.getSignatures()[1];
|
|
||||||
// TODO: DIB, PICT
|
|
||||||
|
|
||||||
public static final byte[] IHDR = new byte[] { 'I', 'H', 'D', 'R' };
|
|
||||||
|
|
||||||
public static final byte[] COMPRESSED1 = { (byte) 0xFE, 0x78, (byte) 0xDA };
|
public static final byte[] COMPRESSED1 = { (byte) 0xFE, 0x78, (byte) 0xDA };
|
||||||
|
|
||||||
public static final byte[] COMPRESSED2 = { (byte) 0xFE, 0x78, (byte) 0x9C };
|
public static final byte[] COMPRESSED2 = { (byte) 0xFE, 0x78, (byte) 0x9C };
|
||||||
|
|
||||||
private int dataBlockStartOfsset;
|
@Deprecated
|
||||||
private int pictureBytesStartOffset;
|
public static final byte[] EMF = { 0x01, 0x00, 0x00, 0x00 };
|
||||||
private int dataBlockSize;
|
|
||||||
private int size;
|
@Deprecated
|
||||||
// private String fileName;
|
public static final byte[] GIF = new byte[] { 'G', 'I', 'F' };
|
||||||
private byte[] rawContent;
|
public static final byte[] IHDR = new byte[] { 'I', 'H', 'D', 'R' };
|
||||||
|
@Deprecated
|
||||||
|
public static final byte[] JPG = new byte[] { (byte) 0xFF, (byte) 0xD8 };
|
||||||
|
private static final POILogger log = POILogFactory
|
||||||
|
.getLogger( Picture.class );
|
||||||
|
@Deprecated
|
||||||
|
public static final byte[] PNG = new byte[] { (byte) 0x89, 0x50, 0x4E,
|
||||||
|
0x47, 0x0D, 0x0A, 0x1A, 0x0A };
|
||||||
|
@Deprecated
|
||||||
|
public static final byte[] TIFF = new byte[] { 0x49, 0x49, 0x2A, 0x00 };
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static final byte[] TIFF1 = new byte[] { 0x4D, 0x4D, 0x00, 0x2A };
|
||||||
|
@Deprecated
|
||||||
|
public static final byte[] WMF1 = { (byte) 0xD7, (byte) 0xCD, (byte) 0xC6,
|
||||||
|
(byte) 0x9A, 0x00, 0x00 };
|
||||||
|
// Windows 3.x
|
||||||
|
@Deprecated
|
||||||
|
public static final byte[] WMF2 = { 0x01, 0x00, 0x09, 0x00, 0x00, 0x03 }; // Windows
|
||||||
|
// 3.x
|
||||||
|
|
||||||
|
private static int getBigEndianInt( byte[] data, int offset )
|
||||||
|
{
|
||||||
|
return ( ( ( data[offset] & 0xFF ) << 24 )
|
||||||
|
+ ( ( data[offset + 1] & 0xFF ) << 16 )
|
||||||
|
+ ( ( data[offset + 2] & 0xFF ) << 8 ) + ( data[offset + 3] & 0xFF ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getBigEndianShort( byte[] data, int offset )
|
||||||
|
{
|
||||||
|
return ( ( ( data[offset] & 0xFF ) << 8 ) + ( data[offset + 1] & 0xFF ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean matchSignature( byte[] pictureData,
|
||||||
|
byte[] signature, int offset )
|
||||||
|
{
|
||||||
|
boolean matched = offset < pictureData.length;
|
||||||
|
for ( int i = 0; ( i + offset ) < pictureData.length
|
||||||
|
&& i < signature.length; i++ )
|
||||||
|
{
|
||||||
|
if ( pictureData[i + offset] != signature[i] )
|
||||||
|
{
|
||||||
|
matched = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PICF _picf;
|
||||||
|
private PICFAndOfficeArtData _picfAndOfficeArtData;
|
||||||
|
|
||||||
private byte[] content;
|
private byte[] content;
|
||||||
private byte[] _dataStream;
|
private int dataBlockStartOfsset;
|
||||||
|
|
||||||
private int height = -1;
|
private int height = -1;
|
||||||
private int width = -1;
|
private int width = -1;
|
||||||
|
|
||||||
public Picture( int dataBlockStartOfsset, byte[] _dataStream,
|
|
||||||
boolean fillBytes )
|
|
||||||
{
|
|
||||||
super( _dataStream, dataBlockStartOfsset );
|
|
||||||
|
|
||||||
this._dataStream = _dataStream;
|
|
||||||
this.dataBlockStartOfsset = dataBlockStartOfsset;
|
|
||||||
this.dataBlockSize = LittleEndian.getInt( _dataStream,
|
|
||||||
dataBlockStartOfsset );
|
|
||||||
this.pictureBytesStartOffset = getPictureBytesStartOffset(
|
|
||||||
dataBlockStartOfsset, _dataStream, dataBlockSize );
|
|
||||||
this.size = dataBlockSize
|
|
||||||
- ( pictureBytesStartOffset - dataBlockStartOfsset );
|
|
||||||
|
|
||||||
if ( size < 0 )
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( fillBytes )
|
|
||||||
{
|
|
||||||
fillImageContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Picture( byte[] _dataStream )
|
public Picture( byte[] _dataStream )
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._dataStream = _dataStream;
|
// XXX: implement
|
||||||
this.dataBlockStartOfsset = 0;
|
// this._dataStream = _dataStream;
|
||||||
this.dataBlockSize = _dataStream.length;
|
// this.dataBlockStartOfsset = 0;
|
||||||
this.pictureBytesStartOffset = 0;
|
// this.dataBlockSize = _dataStream.length;
|
||||||
this.size = _dataStream.length;
|
// this.pictureBytesStartOffset = 0;
|
||||||
|
// this.size = _dataStream.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Picture( int dataBlockStartOfsset, byte[] _dataStream,
|
||||||
|
boolean fillBytes )
|
||||||
|
{
|
||||||
|
_picfAndOfficeArtData = new PICFAndOfficeArtData( _dataStream,
|
||||||
|
dataBlockStartOfsset );
|
||||||
|
_picf = _picfAndOfficeArtData.getPicf();
|
||||||
|
|
||||||
|
this.dataBlockStartOfsset = dataBlockStartOfsset;
|
||||||
|
|
||||||
|
if ( fillBytes )
|
||||||
|
fillImageContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillImageContent()
|
||||||
|
{
|
||||||
|
if ( content != null && content.length > 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
byte[] rawContent = getRawContent();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HACK: Detect compressed images. In reality there should be some way
|
||||||
|
* to determine this from the first 32 bytes, but I can't see any
|
||||||
|
* similarity between all the samples I have obtained, nor any
|
||||||
|
* similarity in the data block contents.
|
||||||
|
*/
|
||||||
|
if ( matchSignature( rawContent, COMPRESSED1, 32 )
|
||||||
|
|| matchSignature( rawContent, COMPRESSED2, 32 ) )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InflaterInputStream in = new InflaterInputStream(
|
||||||
|
new ByteArrayInputStream( rawContent, 33,
|
||||||
|
rawContent.length - 33 ) );
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
int readBytes;
|
||||||
|
while ( ( readBytes = in.read( buf ) ) > 0 )
|
||||||
|
{
|
||||||
|
out.write( buf, 0, readBytes );
|
||||||
|
}
|
||||||
|
content = out.toByteArray();
|
||||||
|
}
|
||||||
|
catch ( IOException e )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Problems reading from the actual ByteArrayInputStream should
|
||||||
|
* never happen so this will only ever be a ZipException.
|
||||||
|
*/
|
||||||
|
log.log( POILogger.INFO,
|
||||||
|
"Possibly corrupt compression or non-compressed data",
|
||||||
|
e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Raw data is not compressed.
|
||||||
|
content = rawContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillJPGWidthHeight()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* http://www.codecomments.com/archive281-2004-3-158083.html
|
||||||
|
*
|
||||||
|
* Algorhitm proposed by Patrick TJ McPhee:
|
||||||
|
*
|
||||||
|
* read 2 bytes make sure they are 'ffd8'x repeatedly: read 2 bytes make
|
||||||
|
* sure the first one is 'ff'x if the second one is 'd9'x stop else if
|
||||||
|
* the second one is c0 or c2 (or possibly other values ...) skip 2
|
||||||
|
* bytes read one byte into depth read two bytes into height read two
|
||||||
|
* bytes into width else read two bytes into length skip forward
|
||||||
|
* length-2 bytes
|
||||||
|
*
|
||||||
|
* Also used Ruby code snippet from:
|
||||||
|
* http://www.bigbold.com/snippets/posts/show/805 for reference
|
||||||
|
*/
|
||||||
|
byte[] jpegContent = getContent();
|
||||||
|
|
||||||
|
int pointer = 2;
|
||||||
|
int firstByte = jpegContent[pointer];
|
||||||
|
int secondByte = jpegContent[pointer + 1];
|
||||||
|
int endOfPicture = jpegContent.length;
|
||||||
|
while ( pointer < endOfPicture - 1 )
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
firstByte = jpegContent[pointer];
|
||||||
|
secondByte = jpegContent[pointer + 1];
|
||||||
|
pointer += 2;
|
||||||
|
}
|
||||||
|
while ( !( firstByte == (byte) 0xFF ) && pointer < endOfPicture - 1 );
|
||||||
|
|
||||||
|
if ( firstByte == ( (byte) 0xFF ) && pointer < endOfPicture - 1 )
|
||||||
|
{
|
||||||
|
if ( secondByte == (byte) 0xD9 || secondByte == (byte) 0xDA )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ( ( secondByte & 0xF0 ) == 0xC0
|
||||||
|
&& secondByte != (byte) 0xC4
|
||||||
|
&& secondByte != (byte) 0xC8
|
||||||
|
&& secondByte != (byte) 0xCC )
|
||||||
|
{
|
||||||
|
pointer += 5;
|
||||||
|
this.height = getBigEndianShort( jpegContent, pointer );
|
||||||
|
this.width = getBigEndianShort( jpegContent, pointer + 2 );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pointer++;
|
||||||
|
pointer++;
|
||||||
|
int length = getBigEndianShort( jpegContent, pointer );
|
||||||
|
pointer += length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pointer++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillPNGWidthHeight()
|
||||||
|
{
|
||||||
|
byte[] pngContent = getContent();
|
||||||
|
/*
|
||||||
|
* Used PNG file format description from
|
||||||
|
* http://www.wotsit.org/download.asp?f=png
|
||||||
|
*/
|
||||||
|
int HEADER_START = PNG.length + 4;
|
||||||
|
if ( matchSignature( pngContent, IHDR, HEADER_START ) )
|
||||||
|
{
|
||||||
|
int IHDR_CHUNK_WIDTH = HEADER_START + 4;
|
||||||
|
this.width = getBigEndianInt( pngContent, IHDR_CHUNK_WIDTH );
|
||||||
|
this.height = getBigEndianInt( pngContent, IHDR_CHUNK_WIDTH + 4 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillWidthHeight()
|
private void fillWidthHeight()
|
||||||
@ -139,48 +288,23 @@ public final class Picture extends PictureDescriptor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to suggest a filename: hex representation of picture structure
|
* @return the horizontal aspect ratio for picture provided by user
|
||||||
* offset in "Data" stream plus extension that is tried to determine from
|
* @deprecated use more precise {@link #getHorizontalScalingFactor()}
|
||||||
* first byte of picture's content.
|
|
||||||
*
|
|
||||||
* @return suggested file name
|
|
||||||
*/
|
*/
|
||||||
public String suggestFullFileName()
|
@Deprecated
|
||||||
|
public int getAspectRatioX()
|
||||||
{
|
{
|
||||||
String fileExt = suggestFileExtension();
|
return _picf.getMx() / 10;
|
||||||
return Integer.toHexString( dataBlockStartOfsset )
|
|
||||||
+ ( fileExt.length() > 0 ? "." + fileExt : "" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes Picture's content bytes to specified OutputStream. Is useful when
|
* @retrn the vertical aspect ratio for picture provided by user
|
||||||
* there is need to write picture bytes directly to stream, omitting its
|
* @deprecated use more precise {@link #getVerticalScalingFactor()}
|
||||||
* representation in memory as distinct byte array.
|
|
||||||
*
|
|
||||||
* @param out
|
|
||||||
* a stream to write to
|
|
||||||
* @throws IOException
|
|
||||||
* if some exception is occured while writing to specified out
|
|
||||||
*/
|
*/
|
||||||
public void writeImageContent( OutputStream out ) throws IOException
|
@Deprecated
|
||||||
|
public int getAspectRatioY()
|
||||||
{
|
{
|
||||||
if ( rawContent != null && rawContent.length > 0 )
|
return _picf.getMy() / 10;
|
||||||
{
|
|
||||||
out.write( rawContent, 0, size );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out.write( _dataStream, pictureBytesStartOffset, size );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The offset of this picture in the picture bytes, used when
|
|
||||||
* matching up with {@link CharacterRun#getPicOffset()}
|
|
||||||
*/
|
|
||||||
public int getStartOffset()
|
|
||||||
{
|
|
||||||
return dataBlockStartOfsset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -193,61 +317,39 @@ public final class Picture extends PictureDescriptor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns picture's content as it stored in Word file, i.e. possibly in
|
* @return The amount the picture has been cropped on the left in twips
|
||||||
* compressed form.
|
|
||||||
*
|
|
||||||
* @return picture's content as it stored in Word file
|
|
||||||
*/
|
|
||||||
public byte[] getRawContent()
|
|
||||||
{
|
|
||||||
fillRawImageContent();
|
|
||||||
return rawContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @return size in bytes of the picture
|
|
||||||
*/
|
|
||||||
public int getSize()
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the horizontal aspect ratio for picture provided by user
|
|
||||||
* @deprecated use more precise {@link #getHorizontalScalingFactor()}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public int getAspectRatioX()
|
public int getDxaCropLeft()
|
||||||
{
|
{
|
||||||
return mx / 10;
|
// TODO: use new properties
|
||||||
|
// if (_picfAndOfficeArtData == null || _picfAndOfficeArtData.getShape()
|
||||||
|
// == null)
|
||||||
|
// return 0;
|
||||||
|
//
|
||||||
|
// final EscherContainerRecord shape = _picfAndOfficeArtData.getShape();
|
||||||
|
// EscherOptRecord optRecord = shape.getChildById( (short) 0xF00B );
|
||||||
|
// if (optRecord == null)
|
||||||
|
// return 0;
|
||||||
|
//
|
||||||
|
// EscherProperty property = optRecord.lookup( 0x0102 );
|
||||||
|
// if (property == null || !(property instanceof EscherSimpleProperty))
|
||||||
|
// return 0;
|
||||||
|
//
|
||||||
|
// EscherSimpleProperty simpleProperty = (EscherSimpleProperty)
|
||||||
|
// property;
|
||||||
|
// return simpleProperty.getPropertyValue();
|
||||||
|
|
||||||
|
return _picf.getDxaReserved1();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Horizontal scaling factor supplied by user expressed in .001%
|
* @return The amount the picture has been cropped on the right in twips
|
||||||
* units
|
|
||||||
*/
|
|
||||||
public int getHorizontalScalingFactor()
|
|
||||||
{
|
|
||||||
return mx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @retrn the vertical aspect ratio for picture provided by user
|
|
||||||
* @deprecated use more precise {@link #getVerticalScalingFactor()}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public int getAspectRatioY()
|
public int getDxaCropRight()
|
||||||
{
|
{
|
||||||
return my / 10;
|
return _picf.getDxaReserved2();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Vertical scaling factor supplied by user expressed in .001% units
|
|
||||||
*/
|
|
||||||
public int getVerticalScalingFactor()
|
|
||||||
{
|
|
||||||
return my;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -258,7 +360,25 @@ public final class Picture extends PictureDescriptor
|
|||||||
*/
|
*/
|
||||||
public int getDxaGoal()
|
public int getDxaGoal()
|
||||||
{
|
{
|
||||||
return dxaGoal;
|
return _picf.getDxaGoal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The amount the picture has been cropped on the bottom in twips
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public int getDyaCropBottom()
|
||||||
|
{
|
||||||
|
return _picf.getDyaReserved2();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The amount the picture has been cropped on the top in twips
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public int getDyaCropTop()
|
||||||
|
{
|
||||||
|
return _picf.getDyaReserved1();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -269,50 +389,29 @@ public final class Picture extends PictureDescriptor
|
|||||||
*/
|
*/
|
||||||
public int getDyaGoal()
|
public int getDyaGoal()
|
||||||
{
|
{
|
||||||
return dyaGoal;
|
return _picf.getDyaGoal();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The amount the picture has been cropped on the left in twips
|
* returns pixel height of the picture or -1 if dimensions determining was
|
||||||
|
* failed
|
||||||
*/
|
*/
|
||||||
public int getDxaCropLeft()
|
public int getHeight()
|
||||||
{
|
{
|
||||||
return dxaCropLeft;
|
if ( height == -1 )
|
||||||
|
{
|
||||||
|
fillWidthHeight();
|
||||||
|
}
|
||||||
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The amount the picture has been cropped on the top in twips
|
* @return Horizontal scaling factor supplied by user expressed in .001%
|
||||||
|
* units
|
||||||
*/
|
*/
|
||||||
public int getDyaCropTop()
|
public int getHorizontalScalingFactor()
|
||||||
{
|
{
|
||||||
return dyaCropTop;
|
return _picf.getMx();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The amount the picture has been cropped on the right in twips
|
|
||||||
*/
|
|
||||||
public int getDxaCropRight()
|
|
||||||
{
|
|
||||||
return dxaCropRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The amount the picture has been cropped on the bottom in twips
|
|
||||||
*/
|
|
||||||
public int getDyaCropBottom()
|
|
||||||
{
|
|
||||||
return dyaCropBottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* tries to suggest extension for picture's file by matching signatures of
|
|
||||||
* popular image formats to first bytes of picture's contents
|
|
||||||
*
|
|
||||||
* @return suggested file extension
|
|
||||||
*/
|
|
||||||
public String suggestFileExtension()
|
|
||||||
{
|
|
||||||
return suggestPictureType().getExtension();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -325,219 +424,56 @@ public final class Picture extends PictureDescriptor
|
|||||||
return suggestPictureType().getMime();
|
return suggestPictureType().getMime();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PictureType suggestPictureType()
|
/**
|
||||||
{
|
* Returns picture's content as it stored in Word file, i.e. possibly in
|
||||||
return PictureType.findMatchingType( getContent() );
|
* compressed form.
|
||||||
}
|
|
||||||
|
|
||||||
// public String getFileName()
|
|
||||||
// {
|
|
||||||
// return fileName;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private static String extractFileName(int blockStartIndex, byte[]
|
|
||||||
// dataStream) {
|
|
||||||
// int fileNameStartOffset = blockStartIndex + 0x7C;
|
|
||||||
// int fileNameSizeOffset = blockStartIndex + FILENAME_SIZE_OFFSET;
|
|
||||||
// int fileNameSize = LittleEndian.getShort(dataStream, fileNameSizeOffset);
|
|
||||||
//
|
|
||||||
// int fileNameIndex = fileNameStartOffset;
|
|
||||||
// char[] fileNameChars = new char[(fileNameSize-1)/2];
|
|
||||||
// int charIndex = 0;
|
|
||||||
// while(charIndex<fileNameChars.length) {
|
|
||||||
// short aChar = LittleEndian.getShort(dataStream, fileNameIndex);
|
|
||||||
// fileNameChars[charIndex] = (char)aChar;
|
|
||||||
// charIndex++;
|
|
||||||
// fileNameIndex += 2;
|
|
||||||
// }
|
|
||||||
// String fileName = new String(fileNameChars);
|
|
||||||
// return fileName.trim();
|
|
||||||
// }
|
|
||||||
|
|
||||||
private void fillRawImageContent()
|
|
||||||
{
|
|
||||||
if ( rawContent != null && rawContent.length > 0 )
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.rawContent = new byte[size];
|
|
||||||
System.arraycopy( _dataStream, pictureBytesStartOffset, rawContent, 0,
|
|
||||||
size );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillImageContent()
|
|
||||||
{
|
|
||||||
if ( content != null && content.length > 0 )
|
|
||||||
return;
|
|
||||||
|
|
||||||
byte[] rawContent = getRawContent();
|
|
||||||
|
|
||||||
// HACK: Detect compressed images. In reality there should be some way
|
|
||||||
// to determine
|
|
||||||
// this from the first 32 bytes, but I can't see any similarity between
|
|
||||||
// all the
|
|
||||||
// samples I have obtained, nor any similarity in the data block
|
|
||||||
// contents.
|
|
||||||
if ( matchSignature( rawContent, COMPRESSED1, 32 )
|
|
||||||
|| matchSignature( rawContent, COMPRESSED2, 32 ) )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
InflaterInputStream in = new InflaterInputStream(
|
|
||||||
new ByteArrayInputStream( rawContent, 33,
|
|
||||||
rawContent.length - 33 ) );
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
byte[] buf = new byte[4096];
|
|
||||||
int readBytes;
|
|
||||||
while ( ( readBytes = in.read( buf ) ) > 0 )
|
|
||||||
{
|
|
||||||
out.write( buf, 0, readBytes );
|
|
||||||
}
|
|
||||||
content = out.toByteArray();
|
|
||||||
}
|
|
||||||
catch ( IOException e )
|
|
||||||
{
|
|
||||||
// Problems reading from the actual ByteArrayInputStream should
|
|
||||||
// never happen
|
|
||||||
// so this will only ever be a ZipException.
|
|
||||||
log.log( POILogger.INFO,
|
|
||||||
"Possibly corrupt compression or non-compressed data",
|
|
||||||
e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Raw data is not compressed.
|
|
||||||
content = rawContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean matchSignature( byte[] pictureData,
|
|
||||||
byte[] signature, int offset )
|
|
||||||
{
|
|
||||||
boolean matched = offset < pictureData.length;
|
|
||||||
for ( int i = 0; ( i + offset ) < pictureData.length
|
|
||||||
&& i < signature.length; i++ )
|
|
||||||
{
|
|
||||||
if ( pictureData[i + offset] != signature[i] )
|
|
||||||
{
|
|
||||||
matched = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matched;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getPictureBytesStartOffset( int dataBlockStartOffset,
|
|
||||||
byte[] _dataStream, int dataBlockSize )
|
|
||||||
{
|
|
||||||
int realPicoffset = dataBlockStartOffset;
|
|
||||||
final int dataBlockEndOffset = dataBlockSize + dataBlockStartOffset;
|
|
||||||
|
|
||||||
// Skip over the PICT block
|
|
||||||
int PICTFBlockSize = LittleEndian.getShort( _dataStream,
|
|
||||||
dataBlockStartOffset + PICT_HEADER_OFFSET ); // Should be 68
|
|
||||||
// bytes
|
|
||||||
|
|
||||||
// Now the PICTF1
|
|
||||||
int PICTF1BlockOffset = PICTFBlockSize + PICT_HEADER_OFFSET;
|
|
||||||
short MM_TYPE = LittleEndian.getShort( _dataStream,
|
|
||||||
dataBlockStartOffset + PICT_HEADER_OFFSET + 2 );
|
|
||||||
if ( MM_TYPE == 0x66 )
|
|
||||||
{
|
|
||||||
// Skip the stPicName
|
|
||||||
int cchPicName = LittleEndian.getUnsignedByte( _dataStream,
|
|
||||||
PICTF1BlockOffset );
|
|
||||||
PICTF1BlockOffset += 1 + cchPicName;
|
|
||||||
}
|
|
||||||
int PICTF1BlockSize = LittleEndian.getShort( _dataStream,
|
|
||||||
dataBlockStartOffset + PICTF1BlockOffset );
|
|
||||||
|
|
||||||
int unknownHeaderOffset = ( PICTF1BlockSize + PICTF1BlockOffset ) < dataBlockEndOffset ? ( PICTF1BlockSize + PICTF1BlockOffset )
|
|
||||||
: PICTF1BlockOffset;
|
|
||||||
realPicoffset += ( unknownHeaderOffset + UNKNOWN_HEADER_SIZE );
|
|
||||||
if ( realPicoffset >= dataBlockEndOffset )
|
|
||||||
{
|
|
||||||
realPicoffset -= UNKNOWN_HEADER_SIZE;
|
|
||||||
}
|
|
||||||
return realPicoffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillJPGWidthHeight()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* http://www.codecomments.com/archive281-2004-3-158083.html
|
|
||||||
*
|
*
|
||||||
* Algorhitm proposed by Patrick TJ McPhee:
|
* @return picture's content as it stored in Word file
|
||||||
*
|
|
||||||
* read 2 bytes make sure they are 'ffd8'x repeatedly: read 2 bytes make
|
|
||||||
* sure the first one is 'ff'x if the second one is 'd9'x stop else if
|
|
||||||
* the second one is c0 or c2 (or possibly other values ...) skip 2
|
|
||||||
* bytes read one byte into depth read two bytes into height read two
|
|
||||||
* bytes into width else read two bytes into length skip forward
|
|
||||||
* length-2 bytes
|
|
||||||
*
|
|
||||||
* Also used Ruby code snippet from:
|
|
||||||
* http://www.bigbold.com/snippets/posts/show/805 for reference
|
|
||||||
*/
|
*/
|
||||||
int pointer = pictureBytesStartOffset + 2;
|
public byte[] getRawContent()
|
||||||
int firstByte = _dataStream[pointer];
|
{
|
||||||
int secondByte = _dataStream[pointer + 1];
|
if ( _picfAndOfficeArtData.getBlipRecords().size() != 1 )
|
||||||
|
return new byte[0];
|
||||||
|
|
||||||
int endOfPicture = pictureBytesStartOffset + size;
|
EscherRecord escherRecord = _picfAndOfficeArtData.getBlipRecords().get(
|
||||||
while ( pointer < endOfPicture - 1 )
|
0 );
|
||||||
|
if ( escherRecord instanceof EscherBlipRecord )
|
||||||
{
|
{
|
||||||
do
|
return ( (EscherBlipRecord) escherRecord ).getPicturedata();
|
||||||
{
|
|
||||||
firstByte = _dataStream[pointer];
|
|
||||||
secondByte = _dataStream[pointer + 1];
|
|
||||||
pointer += 2;
|
|
||||||
}
|
|
||||||
while ( !( firstByte == (byte) 0xFF ) && pointer < endOfPicture - 1 );
|
|
||||||
|
|
||||||
if ( firstByte == ( (byte) 0xFF ) && pointer < endOfPicture - 1 )
|
|
||||||
{
|
|
||||||
if ( secondByte == (byte) 0xD9 || secondByte == (byte) 0xDA )
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if ( ( secondByte & 0xF0 ) == 0xC0
|
|
||||||
&& secondByte != (byte) 0xC4
|
|
||||||
&& secondByte != (byte) 0xC8
|
|
||||||
&& secondByte != (byte) 0xCC )
|
|
||||||
{
|
|
||||||
pointer += 5;
|
|
||||||
this.height = getBigEndianShort( _dataStream, pointer );
|
|
||||||
this.width = getBigEndianShort( _dataStream, pointer + 2 );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pointer++;
|
|
||||||
pointer++;
|
|
||||||
int length = getBigEndianShort( _dataStream, pointer );
|
|
||||||
pointer += length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pointer++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillPNGWidthHeight()
|
if ( escherRecord instanceof EscherBSERecord )
|
||||||
{
|
{
|
||||||
/*
|
return ( (EscherBSERecord) escherRecord ).getBlipRecord()
|
||||||
* Used PNG file format description from
|
.getPicturedata();
|
||||||
* http://www.wotsit.org/download.asp?f=png
|
}
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return size in bytes of the picture
|
||||||
*/
|
*/
|
||||||
int HEADER_START = pictureBytesStartOffset + PNG.length + 4;
|
public int getSize()
|
||||||
if ( matchSignature( _dataStream, IHDR, HEADER_START ) )
|
|
||||||
{
|
{
|
||||||
int IHDR_CHUNK_WIDTH = HEADER_START + 4;
|
return getContent().length;
|
||||||
this.width = getBigEndianInt( _dataStream, IHDR_CHUNK_WIDTH );
|
|
||||||
this.height = getBigEndianInt( _dataStream, IHDR_CHUNK_WIDTH + 4 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The offset of this picture in the picture bytes, used when
|
||||||
|
* matching up with {@link CharacterRun#getPicOffset()}
|
||||||
|
*/
|
||||||
|
public int getStartOffset()
|
||||||
|
{
|
||||||
|
return dataBlockStartOfsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Vertical scaling factor supplied by user expressed in .001% units
|
||||||
|
*/
|
||||||
|
public int getVerticalScalingFactor()
|
||||||
|
{
|
||||||
|
return _picf.getMy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -554,28 +490,106 @@ public final class Picture extends PictureDescriptor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns pixel height of the picture or -1 if dimensions determining was
|
* tries to suggest extension for picture's file by matching signatures of
|
||||||
* failed
|
* popular image formats to first bytes of picture's contents
|
||||||
|
*
|
||||||
|
* @return suggested file extension
|
||||||
*/
|
*/
|
||||||
public int getHeight()
|
public String suggestFileExtension()
|
||||||
{
|
{
|
||||||
if ( height == -1 )
|
return suggestPictureType().getExtension();
|
||||||
{
|
|
||||||
fillWidthHeight();
|
|
||||||
}
|
|
||||||
return height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getBigEndianInt( byte[] data, int offset )
|
/**
|
||||||
|
* Tries to suggest a filename: hex representation of picture structure
|
||||||
|
* offset in "Data" stream plus extension that is tried to determine from
|
||||||
|
* first byte of picture's content.
|
||||||
|
*
|
||||||
|
* @return suggested file name
|
||||||
|
*/
|
||||||
|
public String suggestFullFileName()
|
||||||
{
|
{
|
||||||
return ( ( ( data[offset] & 0xFF ) << 24 )
|
String fileExt = suggestFileExtension();
|
||||||
+ ( ( data[offset + 1] & 0xFF ) << 16 )
|
return Integer.toHexString( dataBlockStartOfsset )
|
||||||
+ ( ( data[offset + 2] & 0xFF ) << 8 ) + ( data[offset + 3] & 0xFF ) );
|
+ ( fileExt.length() > 0 ? "." + fileExt : "" );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getBigEndianShort( byte[] data, int offset )
|
public PictureType suggestPictureType()
|
||||||
{
|
{
|
||||||
return ( ( ( data[offset] & 0xFF ) << 8 ) + ( data[offset + 1] & 0xFF ) );
|
if ( _picfAndOfficeArtData.getBlipRecords().size() != 1 )
|
||||||
|
return PictureType.UNKNOWN;
|
||||||
|
|
||||||
|
EscherRecord escherRecord = _picfAndOfficeArtData.getBlipRecords().get(
|
||||||
|
0 );
|
||||||
|
switch ( escherRecord.getRecordId() )
|
||||||
|
{
|
||||||
|
case (short) 0xF007:
|
||||||
|
{
|
||||||
|
EscherBSERecord bseRecord = (EscherBSERecord) escherRecord;
|
||||||
|
switch ( bseRecord.getBlipTypeWin32() )
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
return PictureType.UNKNOWN;
|
||||||
|
case 0x01:
|
||||||
|
return PictureType.UNKNOWN;
|
||||||
|
case 0x02:
|
||||||
|
return PictureType.EMF;
|
||||||
|
case 0x03:
|
||||||
|
return PictureType.WMF;
|
||||||
|
case 0x04:
|
||||||
|
return PictureType.PICT;
|
||||||
|
case 0x05:
|
||||||
|
return PictureType.JPEG;
|
||||||
|
case 0x06:
|
||||||
|
return PictureType.PNG;
|
||||||
|
case 0x07:
|
||||||
|
return PictureType.BMP;
|
||||||
|
case 0x11:
|
||||||
|
return PictureType.TIFF;
|
||||||
|
case 0x12:
|
||||||
|
return PictureType.JPEG;
|
||||||
|
default:
|
||||||
|
return PictureType.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case (short) 0xF01A:
|
||||||
|
return PictureType.EMF;
|
||||||
|
case (short) 0xF01B:
|
||||||
|
return PictureType.WMF;
|
||||||
|
case (short) 0xF01C:
|
||||||
|
return PictureType.PICT;
|
||||||
|
case (short) 0xF01D:
|
||||||
|
return PictureType.JPEG;
|
||||||
|
case (short) 0xF01E:
|
||||||
|
return PictureType.PNG;
|
||||||
|
case (short) 0xF01F:
|
||||||
|
return PictureType.BMP;
|
||||||
|
case (short) 0xF029:
|
||||||
|
return PictureType.TIFF;
|
||||||
|
case (short) 0xF02A:
|
||||||
|
return PictureType.JPEG;
|
||||||
|
default:
|
||||||
|
return PictureType.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes Picture's content bytes to specified OutputStream. Is useful when
|
||||||
|
* there is need to write picture bytes directly to stream, omitting its
|
||||||
|
* representation in memory as distinct byte array.
|
||||||
|
*
|
||||||
|
* @param out
|
||||||
|
* a stream to write to
|
||||||
|
* @throws IOException
|
||||||
|
* if some exception is occured while writing to specified out
|
||||||
|
*/
|
||||||
|
public void writeImageContent( OutputStream out ) throws IOException
|
||||||
|
{
|
||||||
|
byte[] content = getContent();
|
||||||
|
if ( content != null && content.length > 0 )
|
||||||
|
{
|
||||||
|
out.write( content, 0, content.length );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@ package org.apache.poi.hwpf.usermodel;
|
|||||||
*
|
*
|
||||||
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
|
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
|
||||||
*/
|
*/
|
||||||
public enum PictureType {
|
public enum PictureType
|
||||||
|
{
|
||||||
BMP( "image/bmp", "bmp", new byte[][] { { 'B', 'M' } } ),
|
BMP( "image/bmp", "bmp", new byte[][] { { 'B', 'M' } } ),
|
||||||
|
|
||||||
EMF( "image/x-emf", "emf", new byte[][] { { 0x01, 0x00, 0x00, 0x00 } } ),
|
EMF( "image/x-emf", "emf", new byte[][] { { 0x01, 0x00, 0x00, 0x00 } } ),
|
||||||
@ -30,6 +31,8 @@ public enum PictureType {
|
|||||||
|
|
||||||
JPEG( "image/jpeg", "jpg", new byte[][] { { (byte) 0xFF, (byte) 0xD8 } } ),
|
JPEG( "image/jpeg", "jpg", new byte[][] { { (byte) 0xFF, (byte) 0xD8 } } ),
|
||||||
|
|
||||||
|
PICT( "image/pict", ".pict", new byte[0][] ),
|
||||||
|
|
||||||
PNG( "image/png", "png", new byte[][] { { (byte) 0x89, 0x50, 0x4E, 0x47,
|
PNG( "image/png", "png", new byte[][] { { (byte) 0x89, 0x50, 0x4E, 0x47,
|
||||||
0x0D, 0x0A, 0x1A, 0x0A } } ),
|
0x0D, 0x0A, 0x1A, 0x0A } } ),
|
||||||
|
|
||||||
|
@ -18,17 +18,13 @@ package org.apache.poi.hwpf.usermodel;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
import org.apache.poi.POIDataSamples;
|
import org.apache.poi.POIDataSamples;
|
||||||
import org.apache.poi.hwpf.HWPFDocument;
|
import org.apache.poi.hwpf.HWPFDocument;
|
||||||
@ -41,8 +37,10 @@ import org.apache.poi.hwpf.model.FileInformationBlock;
|
|||||||
import org.apache.poi.hwpf.model.PlexOfField;
|
import org.apache.poi.hwpf.model.PlexOfField;
|
||||||
import org.apache.poi.hwpf.model.SubdocumentType;
|
import org.apache.poi.hwpf.model.SubdocumentType;
|
||||||
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
|
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
|
||||||
|
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
|
||||||
import org.apache.poi.util.IOUtils;
|
import org.apache.poi.util.IOUtils;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.POILogFactory;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test different problems reported in Apache Bugzilla
|
* Test different problems reported in Apache Bugzilla
|
||||||
@ -52,6 +50,8 @@ import org.apache.poi.util.LittleEndian;
|
|||||||
*/
|
*/
|
||||||
public class TestBugs extends TestCase
|
public class TestBugs extends TestCase
|
||||||
{
|
{
|
||||||
|
private static final POILogger logger = POILogFactory
|
||||||
|
.getLogger( TestBugs.class );
|
||||||
|
|
||||||
public static void assertEquals( String expected, String actual )
|
public static void assertEquals( String expected, String actual )
|
||||||
{
|
{
|
||||||
@ -188,11 +188,6 @@ public class TestBugs extends TestCase
|
|||||||
assertNotNull( pic.getContent() );
|
assertNotNull( pic.getContent() );
|
||||||
assertNotNull( pic.getRawContent() );
|
assertNotNull( pic.getRawContent() );
|
||||||
|
|
||||||
// These are probably some sort of offset, need to figure them out
|
|
||||||
assertEquals( 4, pic.getSize() );
|
|
||||||
assertEquals( 0x80000000l, LittleEndian.getUInt( pic.getContent() ) );
|
|
||||||
assertEquals( 0x80000000l, LittleEndian.getUInt( pic.getRawContent() ) );
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a file with empty EMF image, but present Office Drawing
|
* This is a file with empty EMF image, but present Office Drawing
|
||||||
* --sergey
|
* --sergey
|
||||||
@ -688,6 +683,21 @@ public class TestBugs extends TestCase
|
|||||||
WordExtractor wordExtractor = new WordExtractor( hwpfDocument );
|
WordExtractor wordExtractor = new WordExtractor( hwpfDocument );
|
||||||
wordExtractor.getText();
|
wordExtractor.getText();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [FIXED] Bug 51902 - Picture.fillRawImageContent -
|
||||||
|
* ArrayIndexOutOfBoundsException
|
||||||
|
*/
|
||||||
|
public void testBug51890()
|
||||||
|
{
|
||||||
|
HWPFDocument doc = HWPFTestDataSamples.openSampleFile( "Bug51890.doc" );
|
||||||
|
for ( Picture picture : doc.getPicturesTable().getAllPictures() )
|
||||||
|
{
|
||||||
|
PictureType pictureType = picture.suggestPictureType();
|
||||||
|
logger.log( POILogger.DEBUG,
|
||||||
|
"Picture at offset " + picture.getStartOffset()
|
||||||
|
+ " has type " + pictureType );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,24 +212,24 @@ public final class TestPictures extends TestCase {
|
|||||||
assertEquals(4, pictures.size());
|
assertEquals(4, pictures.size());
|
||||||
|
|
||||||
Picture picture = pictures.get( 0 );
|
Picture picture = pictures.get( 0 );
|
||||||
assertEquals("", picture.suggestFileExtension());
|
assertEquals( "emf", picture.suggestFileExtension() );
|
||||||
assertEquals("0", picture.suggestFullFileName());
|
assertEquals( "0.emf", picture.suggestFullFileName() );
|
||||||
assertEquals("image/unknown", picture.getMimeType());
|
assertEquals( "image/x-emf", picture.getMimeType() );
|
||||||
|
|
||||||
picture = pictures.get( 1 );
|
picture = pictures.get( 1 );
|
||||||
assertEquals("", picture.suggestFileExtension());
|
assertEquals( "emf", picture.suggestFileExtension() );
|
||||||
assertEquals("469", picture.suggestFullFileName());
|
assertEquals( "469.emf", picture.suggestFullFileName() );
|
||||||
assertEquals("image/unknown", picture.getMimeType());
|
assertEquals( "image/x-emf", picture.getMimeType() );
|
||||||
|
|
||||||
picture = pictures.get( 2 );
|
picture = pictures.get( 2 );
|
||||||
assertEquals("", picture.suggestFileExtension());
|
assertEquals( "emf", picture.suggestFileExtension() );
|
||||||
assertEquals("8c7", picture.suggestFullFileName());
|
assertEquals( "8c7.emf", picture.suggestFullFileName() );
|
||||||
assertEquals("image/unknown", picture.getMimeType());
|
assertEquals( "image/x-emf", picture.getMimeType() );
|
||||||
|
|
||||||
picture = pictures.get( 3 );
|
picture = pictures.get( 3 );
|
||||||
assertEquals("", picture.suggestFileExtension());
|
assertEquals( "emf", picture.suggestFileExtension() );
|
||||||
assertEquals("10a8", picture.suggestFullFileName());
|
assertEquals( "10a8.emf", picture.suggestFullFileName() );
|
||||||
assertEquals("image/unknown", picture.getMimeType());
|
assertEquals( "image/x-emf", picture.getMimeType() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEquation()
|
public void testEquation()
|
||||||
|
71
src/types/definitions/picf_type.xml
Normal file
71
src/types/definitions/picf_type.xml
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
====================================================================
|
||||||
|
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.
|
||||||
|
====================================================================
|
||||||
|
-->
|
||||||
|
<record fromfile="true" name="PICF" package="org.apache.poi.hwpf.model.types">
|
||||||
|
<suffix>AbstractType</suffix>
|
||||||
|
<description>The PICF structure specifies the type of a picture, as well as the size of the
|
||||||
|
picture and information about its border. <p>Class and fields descriptions are quoted
|
||||||
|
from Microsoft Office Word 97-2007
|
||||||
|
Binary File Format and [MS-DOC] - v20110608 Word (.doc)
|
||||||
|
Binary File Format
|
||||||
|
</description>
|
||||||
|
<author>Sergey Vladimirov; according to Microsoft Office Word 97-2007 Binary File Format
|
||||||
|
Specification [*.doc] and [MS-DOC] - v20110608 Word (.doc) Binary File Format
|
||||||
|
</author>
|
||||||
|
<fields>
|
||||||
|
<field type="int" size="4" name="lcb"
|
||||||
|
description="A signed integer that specifies the size, in bytes, of this PICF structure and the subsequent data"/>
|
||||||
|
<field type="int" size="2" name="cbHeader"
|
||||||
|
description="An unsigned integer that specifies the size, in bytes, of this PICF structure. This value MUST be 0x44"/>
|
||||||
|
|
||||||
|
<!-- mfpf (8 bytes) -->
|
||||||
|
<field type="short" size="2" name="mm"
|
||||||
|
description="A signed integer that specifies the format of the picture data"/>
|
||||||
|
<field type="short" size="2" name="xExt" description="This field is unused and MUST be ignored"/>
|
||||||
|
<field type="short" size="2" name="yExt" description="This field is unused and MUST be ignored"/>
|
||||||
|
<field type="short" size="2" name="swHMF" description="This field is unused and MUST be ignored"/>
|
||||||
|
|
||||||
|
<!-- innerHeader (14 bytes) -->
|
||||||
|
<field type="int" size="4" name="grf" description="This field MUST be ignored"/>
|
||||||
|
<field type="int" size="4" name="padding" description="This value MUST be zero and MUST be ignored"/>
|
||||||
|
<field type="int" size="2" name="mmPM" description="This field MUST be ignored"/>
|
||||||
|
<field type="int" size="4" name="padding2" description="This value MUST be zero and MUST be ignored"/>
|
||||||
|
|
||||||
|
<!-- picmid (38 bytes): A PICMID structure that specifies the size and border information of
|
||||||
|
the picture. -->
|
||||||
|
<field type="short" size="2" name="dxaGoal"/>
|
||||||
|
<field type="short" size="2" name="dyaGoal"/>
|
||||||
|
<field type="int" size="2" name="mx"/>
|
||||||
|
<field type="int" size="2" name="my"/>
|
||||||
|
<field type="short" size="2" name="dxaReserved1"/>
|
||||||
|
<field type="short" size="2" name="dyaReserved1"/>
|
||||||
|
<field type="short" size="2" name="dxaReserved2"/>
|
||||||
|
<field type="short" size="2" name="dyaReserved2"/>
|
||||||
|
<field type="byte" size="1" name="fReserved"/>
|
||||||
|
<field type="byte" size="1" name="bpp"/>
|
||||||
|
<field type="byte[]" size="4" name="brcTop80"/>
|
||||||
|
<field type="byte[]" size="4" name="brcLeft80"/>
|
||||||
|
<field type="byte[]" size="4" name="brcBottom80"/>
|
||||||
|
<field type="byte[]" size="4" name="brcRight80"/>
|
||||||
|
<field type="short" size="2" name="dxaReserved3"/>
|
||||||
|
<field type="short" size="2" name="dyaReserved3"/>
|
||||||
|
|
||||||
|
<field type="short" size="2" name="cProps" description="This value MUST be 0 and MUST be ignored"/>
|
||||||
|
</fields>
|
||||||
|
</record>
|
Loading…
Reference in New Issue
Block a user