git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1771812 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_16_BETA2
@@ -0,0 +1,57 @@ | |||
/* ==================================================================== | |||
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.sl.image; | |||
import java.awt.Dimension; | |||
import java.awt.image.BufferedImage; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import javax.imageio.ImageIO; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.util.Units; | |||
@Internal | |||
public class ImageHeaderBitmap { | |||
private static final POILogger LOG = POILogFactory.getLogger(ImageHeaderBitmap.class); | |||
private final Dimension size; | |||
public ImageHeaderBitmap(byte data[], int offset) { | |||
BufferedImage img = null; | |||
try { | |||
img = ImageIO.read(new ByteArrayInputStream(data, offset, data.length-offset)); | |||
} catch (IOException e) { | |||
LOG.log(POILogger.WARN, "Can't determine image dimensions", e); | |||
} | |||
// set dummy size, in case of dummy dimension can't be set | |||
size = (img == null) | |||
? new Dimension(200,200) | |||
: new Dimension( | |||
(int)Units.pixelToPoints(img.getWidth()), | |||
(int)Units.pixelToPoints(img.getHeight()) | |||
); | |||
} | |||
public Dimension getSize() { | |||
return size; | |||
} | |||
} |
@@ -0,0 +1,68 @@ | |||
/* ==================================================================== | |||
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.sl.image; | |||
import java.awt.Dimension; | |||
import java.awt.Rectangle; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LocaleUtil; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
@Internal | |||
public class ImageHeaderEMF { | |||
private static POILogger LOG = POILogFactory.getLogger(ImageHeaderEMF.class); | |||
private final static String EMF_SIGNATURE = " EMF"; // 0x464D4520 (LE) | |||
// rectangular inclusive-inclusive bounds, in device units, of the smallest | |||
// rectangle that can be drawn around the image stored in the metafile. | |||
private final Rectangle deviceBounds; | |||
public ImageHeaderEMF(byte data[], int offset) { | |||
int type = (int)LittleEndian.getUInt(data, offset); offset += 4; | |||
if (type != 1) { | |||
LOG.log(POILogger.WARN, "Invalid EMF picture - invalid type"); | |||
deviceBounds = new Rectangle(0,0,200,200); | |||
return; | |||
} | |||
// ignore header size | |||
offset += 4; | |||
int left = LittleEndian.getInt(data, offset); offset += 4; | |||
int top = LittleEndian.getInt(data, offset); offset += 4; | |||
int right = LittleEndian.getInt(data, offset); offset += 4; | |||
int bottom = LittleEndian.getInt(data, offset); offset += 4; | |||
deviceBounds = new Rectangle(left, top, right-left, bottom-top); | |||
// ignore frame bounds | |||
offset += 16; | |||
String signature = new String(data, offset, EMF_SIGNATURE.length(), LocaleUtil.CHARSET_1252); | |||
if (!EMF_SIGNATURE.equals(signature)) { | |||
LOG.log(POILogger.WARN, "Invalid EMF picture - invalid signature"); | |||
} | |||
} | |||
public Dimension getSize() { | |||
return deviceBounds.getSize(); | |||
} | |||
public Rectangle getBounds() { | |||
return deviceBounds; | |||
} | |||
} |
@@ -0,0 +1,103 @@ | |||
/* ==================================================================== | |||
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.sl.image; | |||
import java.awt.Dimension; | |||
import java.awt.Rectangle; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.util.Units; | |||
@Internal | |||
public class ImageHeaderPICT { | |||
/** | |||
* skip the first 512 bytes - they are MAC specific crap | |||
*/ | |||
public static final int PICT_HEADER_OFFSET = 512; | |||
public static final double DEFAULT_RESOLUTION = Units.POINT_DPI; | |||
private static final byte V2_HEADER[] = { | |||
0x00, 0x11, // v2 version opcode | |||
0x02, (byte)0xFF, // version number of new picture | |||
0x0C, 0x00, // header opcode | |||
(byte)0xFF, (byte)0xFE, 0x00, 0x00 // pic size dummy | |||
}; | |||
private final Rectangle bounds; | |||
private final double hRes, vRes; | |||
public ImageHeaderPICT(byte data[], int offset) { | |||
// http://mirrors.apple2.org.za/apple.cabi.net/Graphics/PICT.and_QT.INFO/PICT.file.format.TI.txt | |||
// low order 16 bits of picture size - can be ignored | |||
offset += 2; | |||
// rectangular bounding box of picture, at 72 dpi | |||
// rect : 8 bytes (top, left, bottom, right: integer) | |||
int y1 = readUnsignedShort(data, offset); offset += 2; | |||
int x1 = readUnsignedShort(data, offset); offset += 2; | |||
int y2 = readUnsignedShort(data, offset); offset += 2; | |||
int x2 = readUnsignedShort(data, offset); offset += 2; | |||
// check for version 2 ... otherwise we don't read any further | |||
boolean isV2 = true; | |||
for (byte b : V2_HEADER) { | |||
if (b != data[offset++]) { | |||
isV2 = false; | |||
break; | |||
} | |||
} | |||
if (isV2) { | |||
// 4 bytes - fixed, horizontal resolution (dpi) of source data | |||
hRes = readFixedPoint(data, offset); offset += 4; | |||
// 4 bytes - fixed, vertical resolution (dpi) of source data | |||
vRes = readFixedPoint(data, offset); offset += 4; | |||
} else { | |||
hRes = DEFAULT_RESOLUTION; | |||
vRes = DEFAULT_RESOLUTION; | |||
} | |||
bounds = new Rectangle(x1,y1,x2-x1,y2-y1); | |||
} | |||
public Dimension getSize() { | |||
int height = (int)Math.round(bounds.height*DEFAULT_RESOLUTION/vRes); | |||
int width = (int)Math.round(bounds.width*DEFAULT_RESOLUTION/hRes); | |||
return new Dimension(width, height); | |||
} | |||
public Rectangle getBounds() { | |||
return bounds; | |||
} | |||
private static int readUnsignedShort(byte data[], int offset) { | |||
int b0 = data[offset] & 0xFF; | |||
int b1 = data[offset+1] & 0xFF; | |||
return b0 << 8 | b1; | |||
} | |||
private static double readFixedPoint(byte data[], int offset) { | |||
int b0 = data[offset] & 0xFF; | |||
int b1 = data[offset+1] & 0xFF; | |||
int b2 = data[offset+2] & 0xFF; | |||
int b3 = data[offset+3] & 0xFF; | |||
int i = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; | |||
return i / (double)0x10000; | |||
} | |||
} |
@@ -0,0 +1,154 @@ | |||
/* ==================================================================== | |||
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.sl.image; | |||
import java.awt.Dimension; | |||
import java.awt.Rectangle; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.util.Units; | |||
/** | |||
* Aldus Placeable Metafile header - 22 byte structure before WMF data. | |||
* <ul> | |||
* <li>int Key; Magic number (always 9AC6CDD7h) | |||
* <li>short Handle; Metafile HANDLE number (always 0) | |||
* <li>short Left; Left coordinate in metafile units | |||
* <li>short Top; Top coordinate in metafile units | |||
* <li>short Right; Right coordinate in metafile units | |||
* <li>short Bottom; Bottom coordinate in metafile units | |||
* <li>short Inch; Number of metafile units per inch | |||
* <li>int Reserved; Reserved (always 0) | |||
* <li>short Checksum; Checksum value for previous 10 shorts | |||
* </ul> | |||
*/ | |||
@Internal | |||
public class ImageHeaderWMF { | |||
public static final int APMHEADER_KEY = 0x9AC6CDD7; | |||
private static POILogger LOG = POILogFactory.getLogger(ImageHeaderWMF.class); | |||
@SuppressWarnings("unused") | |||
private final int handle; | |||
private final int left, top, right, bottom; | |||
/** | |||
* The number of logical units per inch used to represent the image. | |||
* This value can be used to scale an image. By convention, an image is | |||
* considered to be recorded at 1440 logical units (twips) per inch. | |||
* Thus, a value of 720 specifies that the image SHOULD be rendered at | |||
* twice its normal size, and a value of 2880 specifies that the image | |||
* SHOULD be rendered at half its normal size. | |||
*/ | |||
private final int inch; | |||
@SuppressWarnings("unused") | |||
private final int reserved; | |||
private int checksum; | |||
public ImageHeaderWMF(Rectangle dim) { | |||
handle = 0; | |||
left = dim.x; | |||
top = dim.y; | |||
right = dim.x + dim.width; | |||
bottom = dim.y + dim.height; | |||
inch = Units.POINT_DPI; //default resolution is 72 dpi | |||
reserved = 0; | |||
} | |||
public ImageHeaderWMF(byte[] data, int pos) { | |||
int key = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE; //header key | |||
if (key != APMHEADER_KEY) { | |||
LOG.log(POILogger.WARN, "WMF file doesn't contain a placeable header - ignore parsing"); | |||
handle = 0; | |||
left = 0; | |||
top = 0; | |||
right = 200; | |||
bottom = 200; | |||
inch = Units.POINT_DPI; //default resolution is 72 dpi | |||
reserved = 0; | |||
return; | |||
} | |||
handle = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
left = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
top = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
right = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
bottom = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
inch = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
reserved = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE; | |||
checksum = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
if (checksum != getChecksum()){ | |||
LOG.log(POILogger.WARN, "WMF checksum does not match the header data"); | |||
} | |||
} | |||
/** | |||
* Returns a checksum value for the previous 10 shorts in the header. | |||
* The checksum is calculated by XORing each short value to an initial value of 0: | |||
*/ | |||
public int getChecksum(){ | |||
int cs = 0; | |||
cs ^= (APMHEADER_KEY & 0x0000FFFF); | |||
cs ^= ((APMHEADER_KEY & 0xFFFF0000) >> 16); | |||
cs ^= left; | |||
cs ^= top; | |||
cs ^= right; | |||
cs ^= bottom; | |||
cs ^= inch; | |||
return cs; | |||
} | |||
public void write(OutputStream out) throws IOException { | |||
byte[] header = new byte[22]; | |||
int pos = 0; | |||
LittleEndian.putInt(header, pos, APMHEADER_KEY); pos += LittleEndian.INT_SIZE; //header key | |||
LittleEndian.putUShort(header, pos, 0); pos += LittleEndian.SHORT_SIZE; //hmf | |||
LittleEndian.putUShort(header, pos, left); pos += LittleEndian.SHORT_SIZE; //left | |||
LittleEndian.putUShort(header, pos, top); pos += LittleEndian.SHORT_SIZE; //top | |||
LittleEndian.putUShort(header, pos, right); pos += LittleEndian.SHORT_SIZE; //right | |||
LittleEndian.putUShort(header, pos, bottom); pos += LittleEndian.SHORT_SIZE; //bottom | |||
LittleEndian.putUShort(header, pos, inch); pos += LittleEndian.SHORT_SIZE; //inch | |||
LittleEndian.putInt(header, pos, 0); pos += LittleEndian.INT_SIZE; //reserved | |||
checksum = getChecksum(); | |||
LittleEndian.putUShort(header, pos, checksum); | |||
out.write(header); | |||
} | |||
public Dimension getSize() { | |||
//coefficient to translate from WMF dpi to 72dpi | |||
double coeff = ((double)Units.POINT_DPI)/inch; | |||
return new Dimension((int)Math.round((right-left)*coeff), (int)Math.round((bottom-top)*coeff)); | |||
} | |||
public Rectangle getBounds() { | |||
return new Rectangle(left, top, right-left, bottom-top); | |||
} | |||
public int getLength(){ | |||
return 22; | |||
} | |||
} |
@@ -20,28 +20,23 @@ | |||
package org.apache.poi.xslf.usermodel; | |||
import java.awt.Dimension; | |||
import java.awt.image.BufferedImage; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import javax.imageio.ImageIO; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.apache.poi.POIXMLException; | |||
import org.apache.poi.hslf.blip.EMF; | |||
import org.apache.poi.hslf.blip.PICT; | |||
import org.apache.poi.hslf.blip.WMF; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.openxml4j.opc.PackageRelationship; | |||
import org.apache.poi.sl.image.ImageHeaderBitmap; | |||
import org.apache.poi.sl.image.ImageHeaderEMF; | |||
import org.apache.poi.sl.image.ImageHeaderPICT; | |||
import org.apache.poi.sl.image.ImageHeaderWMF; | |||
import org.apache.poi.sl.usermodel.PictureData; | |||
import org.apache.poi.util.Beta; | |||
import org.apache.poi.util.IOUtils; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.util.Units; | |||
/** | |||
@@ -49,8 +44,6 @@ import org.apache.poi.util.Units; | |||
*/ | |||
@Beta | |||
public final class XSLFPictureData extends POIXMLDocumentPart implements PictureData { | |||
private static final POILogger logger = POILogFactory.getLogger(XSLFPictureData.class); | |||
private Long checksum = null; | |||
// original image dimensions (for formats supported by BufferedImage) | |||
@@ -171,30 +164,18 @@ public final class XSLFPictureData extends POIXMLDocumentPart implements Picture | |||
switch (pt) { | |||
case EMF: | |||
origSize = new EMF.NativeHeader(data, 0).getSize(); | |||
origSize = new ImageHeaderEMF(data, 0).getSize(); | |||
break; | |||
case WMF: | |||
// wmf files in pptx usually have their placeable header | |||
// stripped away, so this returns only the dummy size | |||
origSize = new WMF.NativeHeader(data, 0).getSize(); | |||
origSize = new ImageHeaderWMF(data, 0).getSize(); | |||
break; | |||
case PICT: | |||
origSize = new PICT.NativeHeader(data, 0).getSize(); | |||
origSize = new ImageHeaderPICT(data, 0).getSize(); | |||
break; | |||
default: | |||
BufferedImage img = null; | |||
try { | |||
img = ImageIO.read(new ByteArrayInputStream(data)); | |||
} catch (IOException e) { | |||
logger.log(POILogger.WARN, "Can't determine image dimensions", e); | |||
} | |||
// set dummy size, in case of dummy dimension can't be set | |||
origSize = (img == null) | |||
? new Dimension(200,200) | |||
: new Dimension( | |||
(int)Units.pixelToPoints(img.getWidth()), | |||
(int)Units.pixelToPoints(img.getHeight()) | |||
); | |||
origSize = new ImageHeaderBitmap(data, 0).getSize(); | |||
break; | |||
} | |||
} |
@@ -18,7 +18,6 @@ | |||
package org.apache.poi.hslf.blip; | |||
import java.awt.Dimension; | |||
import java.awt.Rectangle; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
@@ -26,8 +25,7 @@ import java.io.InputStream; | |||
import java.util.zip.InflaterInputStream; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LocaleUtil; | |||
import org.apache.poi.sl.image.ImageHeaderEMF; | |||
import org.apache.poi.util.Units; | |||
/** | |||
@@ -35,38 +33,6 @@ import org.apache.poi.util.Units; | |||
*/ | |||
public final class EMF extends Metafile { | |||
public static class NativeHeader { | |||
// rectangular inclusive-inclusive bounds, in device units, of the smallest | |||
// rectangle that can be drawn around the image stored in the metafile. | |||
private final Rectangle deviceBounds; | |||
private final static String EMF_SIGNATURE = " EMF"; // 0x464D4520 (LE) | |||
public NativeHeader(byte data[], int offset) { | |||
int type = (int)LittleEndian.getUInt(data, offset); offset += 4; | |||
if (type != 1) { | |||
throw new HSLFException("Invalid EMF picture"); | |||
} | |||
// ignore header size | |||
offset += 4; | |||
int left = LittleEndian.getInt(data, offset); offset += 4; | |||
int top = LittleEndian.getInt(data, offset); offset += 4; | |||
int right = LittleEndian.getInt(data, offset); offset += 4; | |||
int bottom = LittleEndian.getInt(data, offset); offset += 4; | |||
deviceBounds = new Rectangle(left, top, right-left, bottom-top); | |||
// ignore frame bounds | |||
offset += 16; | |||
String signature = new String(data, offset, EMF_SIGNATURE.length(), LocaleUtil.CHARSET_1252); | |||
if (!EMF_SIGNATURE.equals(signature)) { | |||
throw new HSLFException("Invalid EMF picture"); | |||
} | |||
} | |||
public Dimension getSize() { | |||
return deviceBounds.getSize(); | |||
} | |||
} | |||
@Override | |||
public byte[] getData(){ | |||
try { | |||
@@ -96,11 +62,11 @@ public final class EMF extends Metafile { | |||
public void setData(byte[] data) throws IOException { | |||
byte[] compressed = compress(data, 0, data.length); | |||
NativeHeader nHeader = new NativeHeader(data, 0); | |||
ImageHeaderEMF nHeader = new ImageHeaderEMF(data, 0); | |||
Header header = new Header(); | |||
header.wmfsize = data.length; | |||
header.bounds = nHeader.deviceBounds; | |||
header.bounds = nHeader.getBounds(); | |||
Dimension nDim = nHeader.getSize(); | |||
header.size = new Dimension(Units.toEMU(nDim.getWidth()), Units.toEMU(nDim.getHeight())); | |||
header.zipsize = compressed.length; |
@@ -18,13 +18,13 @@ | |||
package org.apache.poi.hslf.blip; | |||
import java.awt.Dimension; | |||
import java.awt.Rectangle; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.util.zip.InflaterInputStream; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.sl.image.ImageHeaderPICT; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.util.Units; | |||
@@ -35,79 +35,6 @@ import org.apache.poi.util.Units; | |||
public final class PICT extends Metafile { | |||
private static final POILogger LOG = POILogFactory.getLogger(PICT.class); | |||
public static class NativeHeader { | |||
/** | |||
* skip the first 512 bytes - they are MAC specific crap | |||
*/ | |||
public static final int PICT_HEADER_OFFSET = 512; | |||
public static final double DEFAULT_RESOLUTION = Units.POINT_DPI; | |||
private static final byte V2_HEADER[] = { | |||
0x00, 0x11, // v2 version opcode | |||
0x02, (byte)0xFF, // version number of new picture | |||
0x0C, 0x00, // header opcode | |||
(byte)0xFF, (byte)0xFE, 0x00, 0x00 // pic size dummy | |||
}; | |||
public final Rectangle bounds; | |||
public final double hRes, vRes; | |||
public NativeHeader(byte data[], int offset) { | |||
// http://mirrors.apple2.org.za/apple.cabi.net/Graphics/PICT.and_QT.INFO/PICT.file.format.TI.txt | |||
// low order 16 bits of picture size - can be ignored | |||
offset += 2; | |||
// rectangular bounding box of picture, at 72 dpi | |||
// rect : 8 bytes (top, left, bottom, right: integer) | |||
int y1 = readUnsignedShort(data, offset); offset += 2; | |||
int x1 = readUnsignedShort(data, offset); offset += 2; | |||
int y2 = readUnsignedShort(data, offset); offset += 2; | |||
int x2 = readUnsignedShort(data, offset); offset += 2; | |||
// check for version 2 ... otherwise we don't read any further | |||
boolean isV2 = true; | |||
for (byte b : V2_HEADER) { | |||
if (b != data[offset++]) { | |||
isV2 = false; | |||
break; | |||
} | |||
} | |||
if (isV2) { | |||
// 4 bytes - fixed, horizontal resolution (dpi) of source data | |||
hRes = readFixedPoint(data, offset); offset += 4; | |||
// 4 bytes - fixed, vertical resolution (dpi) of source data | |||
vRes = readFixedPoint(data, offset); offset += 4; | |||
} else { | |||
hRes = DEFAULT_RESOLUTION; | |||
vRes = DEFAULT_RESOLUTION; | |||
} | |||
bounds = new Rectangle(x1,y1,x2-x1,y2-y1); | |||
} | |||
public Dimension getSize() { | |||
int height = (int)Math.round(bounds.height*DEFAULT_RESOLUTION/vRes); | |||
int width = (int)Math.round(bounds.width*DEFAULT_RESOLUTION/hRes); | |||
return new Dimension(width, height); | |||
} | |||
private static int readUnsignedShort(byte data[], int offset) { | |||
int b0 = data[offset] & 0xFF; | |||
int b1 = data[offset+1] & 0xFF; | |||
return b0 << 8 | b1; | |||
} | |||
private static double readFixedPoint(byte data[], int offset) { | |||
int b0 = data[offset] & 0xFF; | |||
int b1 = data[offset+1] & 0xFF; | |||
int b2 = data[offset+2] & 0xFF; | |||
int b3 = data[offset+3] & 0xFF; | |||
int i = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; | |||
return i / (double)0x10000; | |||
} | |||
} | |||
@Override | |||
public byte[] getData(){ | |||
@@ -163,14 +90,14 @@ public final class PICT extends Metafile { | |||
@Override | |||
public void setData(byte[] data) throws IOException { | |||
// skip the first 512 bytes - they are MAC specific crap | |||
final int nOffset = NativeHeader.PICT_HEADER_OFFSET; | |||
NativeHeader nHeader = new NativeHeader(data, nOffset); | |||
final int nOffset = ImageHeaderPICT.PICT_HEADER_OFFSET; | |||
ImageHeaderPICT nHeader = new ImageHeaderPICT(data, nOffset); | |||
Header header = new Header(); | |||
header.wmfsize = data.length - nOffset; | |||
byte[] compressed = compress(data, nOffset, header.wmfsize); | |||
header.zipsize = compressed.length; | |||
header.bounds = nHeader.bounds; | |||
header.bounds = nHeader.getBounds(); | |||
Dimension nDim = nHeader.getSize(); | |||
header.size = new Dimension(Units.toEMU(nDim.getWidth()), Units.toEMU(nDim.getHeight())); | |||
@@ -18,18 +18,14 @@ | |||
package org.apache.poi.hslf.blip; | |||
import java.awt.Dimension; | |||
import java.awt.Rectangle; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.util.zip.InflaterInputStream; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.sl.image.ImageHeaderWMF; | |||
import org.apache.poi.util.Units; | |||
/** | |||
@@ -49,7 +45,7 @@ public final class WMF extends Metafile { | |||
long len = is.skip(header.getSize() + CHECKSUM_SIZE*uidInstanceCount); | |||
assert(len == header.getSize() + CHECKSUM_SIZE*uidInstanceCount); | |||
NativeHeader aldus = new NativeHeader(header.bounds); | |||
ImageHeaderWMF aldus = new ImageHeaderWMF(header.bounds); | |||
aldus.write(out); | |||
InflaterInputStream inflater = new InflaterInputStream( is ); | |||
@@ -68,14 +64,14 @@ public final class WMF extends Metafile { | |||
@Override | |||
public void setData(byte[] data) throws IOException { | |||
int pos = 0; | |||
NativeHeader nHeader = new NativeHeader(data, pos); | |||
ImageHeaderWMF nHeader = new ImageHeaderWMF(data, pos); | |||
pos += nHeader.getLength(); | |||
byte[] compressed = compress(data, pos, data.length-pos); | |||
Header header = new Header(); | |||
header.wmfsize = data.length - nHeader.getLength(); | |||
header.bounds = new Rectangle((short)nHeader.left, (short)nHeader.top, (short)nHeader.right-(short)nHeader.left, (short)nHeader.bottom-(short)nHeader.top); | |||
header.bounds = nHeader.getBounds(); | |||
Dimension nDim = nHeader.getSize(); | |||
header.size = new Dimension(Units.toEMU(nDim.getWidth()), Units.toEMU(nDim.getHeight())); | |||
header.zipsize = compressed.length; | |||
@@ -118,122 +114,4 @@ public final class WMF extends Metafile { | |||
throw new IllegalArgumentException(signature+" is not a valid instance/signature value for WMF"); | |||
} | |||
} | |||
/** | |||
* Aldus Placeable Metafile header - 22 byte structure before WMF data. | |||
* <ul> | |||
* <li>int Key; Magic number (always 9AC6CDD7h) | |||
* <li>short Handle; Metafile HANDLE number (always 0) | |||
* <li>short Left; Left coordinate in metafile units | |||
* <li>short Top; Top coordinate in metafile units | |||
* <li>short Right; Right coordinate in metafile units | |||
* <li>short Bottom; Bottom coordinate in metafile units | |||
* <li>short Inch; Number of metafile units per inch | |||
* <li>int Reserved; Reserved (always 0) | |||
* <li>short Checksum; Checksum value for previous 10 shorts | |||
* </ul> | |||
*/ | |||
@SuppressWarnings("unused") | |||
public static class NativeHeader { | |||
public static final int APMHEADER_KEY = 0x9AC6CDD7; | |||
private static POILogger logger = POILogFactory.getLogger(NativeHeader.class); | |||
private final int handle; | |||
private final int left, top, right, bottom; | |||
/** | |||
* The number of logical units per inch used to represent the image. | |||
* This value can be used to scale an image. By convention, an image is | |||
* considered to be recorded at 1440 logical units (twips) per inch. | |||
* Thus, a value of 720 specifies that the image SHOULD be rendered at | |||
* twice its normal size, and a value of 2880 specifies that the image | |||
* SHOULD be rendered at half its normal size. | |||
*/ | |||
private final int inch; | |||
private final int reserved; | |||
private int checksum; | |||
public NativeHeader(Rectangle dim) { | |||
handle = 0; | |||
left = dim.x; | |||
top = dim.y; | |||
right = dim.x + dim.width; | |||
bottom = dim.y + dim.height; | |||
inch = Units.POINT_DPI; //default resolution is 72 dpi | |||
reserved = 0; | |||
} | |||
public NativeHeader(byte[] data, int pos) { | |||
int key = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE; //header key | |||
if (key != APMHEADER_KEY) { | |||
logger.log(POILogger.WARN, "WMF file doesn't contain a placeable header - ignore parsing"); | |||
handle = 0; | |||
left = 0; | |||
top = 0; | |||
right = 200; | |||
bottom = 200; | |||
inch = Units.POINT_DPI; //default resolution is 72 dpi | |||
reserved = 0; | |||
return; | |||
} | |||
handle = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
left = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
top = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
right = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
bottom = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
inch = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
reserved = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE; | |||
checksum = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; | |||
if (checksum != getChecksum()){ | |||
logger.log(POILogger.WARN, "WMF checksum does not match the header data"); | |||
} | |||
} | |||
/** | |||
* Returns a checksum value for the previous 10 shorts in the header. | |||
* The checksum is calculated by XORing each short value to an initial value of 0: | |||
*/ | |||
public int getChecksum(){ | |||
int cs = 0; | |||
cs ^= (APMHEADER_KEY & 0x0000FFFF); | |||
cs ^= ((APMHEADER_KEY & 0xFFFF0000) >> 16); | |||
cs ^= left; | |||
cs ^= top; | |||
cs ^= right; | |||
cs ^= bottom; | |||
cs ^= inch; | |||
return cs; | |||
} | |||
public void write(OutputStream out) throws IOException { | |||
byte[] header = new byte[22]; | |||
int pos = 0; | |||
LittleEndian.putInt(header, pos, APMHEADER_KEY); pos += LittleEndian.INT_SIZE; //header key | |||
LittleEndian.putUShort(header, pos, 0); pos += LittleEndian.SHORT_SIZE; //hmf | |||
LittleEndian.putUShort(header, pos, left); pos += LittleEndian.SHORT_SIZE; //left | |||
LittleEndian.putUShort(header, pos, top); pos += LittleEndian.SHORT_SIZE; //top | |||
LittleEndian.putUShort(header, pos, right); pos += LittleEndian.SHORT_SIZE; //right | |||
LittleEndian.putUShort(header, pos, bottom); pos += LittleEndian.SHORT_SIZE; //bottom | |||
LittleEndian.putUShort(header, pos, inch); pos += LittleEndian.SHORT_SIZE; //inch | |||
LittleEndian.putInt(header, pos, 0); pos += LittleEndian.INT_SIZE; //reserved | |||
checksum = getChecksum(); | |||
LittleEndian.putUShort(header, pos, checksum); | |||
out.write(header); | |||
} | |||
public Dimension getSize() { | |||
//coefficient to translate from WMF dpi to 72dpi | |||
double coeff = ((double)Units.POINT_DPI)/inch; | |||
return new Dimension((int)Math.round((right-left)*coeff), (int)Math.round((bottom-top)*coeff)); | |||
} | |||
public int getLength(){ | |||
return 22; | |||
} | |||
} | |||
} |
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; | |||
import java.awt.Dimension; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.net.URL; | |||
import java.util.List; | |||
@@ -34,6 +35,9 @@ import org.apache.poi.hslf.blip.JPEG; | |||
import org.apache.poi.hslf.blip.PICT; | |||
import org.apache.poi.hslf.blip.PNG; | |||
import org.apache.poi.hslf.blip.WMF; | |||
import org.apache.poi.sl.image.ImageHeaderEMF; | |||
import org.apache.poi.sl.image.ImageHeaderPICT; | |||
import org.apache.poi.sl.image.ImageHeaderWMF; | |||
import org.apache.poi.sl.usermodel.PictureData.PictureType; | |||
import org.apache.poi.util.Units; | |||
import org.junit.Ignore; | |||
@@ -51,13 +55,13 @@ public final class TestPictures { | |||
* Test read/write Macintosh PICT | |||
*/ | |||
@Test | |||
public void testPICT() throws Exception { | |||
public void testPICT() throws IOException { | |||
HSLFSlideShow ppt = new HSLFSlideShow(); | |||
HSLFSlide slide = ppt.createSlide(); | |||
byte[] src_bytes = slTests.readFile("cow.pict"); | |||
HSLFPictureData data = ppt.addPicture(src_bytes, PictureType.PICT); | |||
PICT.NativeHeader nHeader = new PICT.NativeHeader(src_bytes, 512); | |||
ImageHeaderPICT nHeader = new ImageHeaderPICT(src_bytes, 512); | |||
final int expWidth = 197, expHeight = 137; | |||
Dimension nDim = nHeader.getSize(); | |||
assertEquals(expWidth, nDim.getWidth(), 0); | |||
@@ -114,13 +118,13 @@ public final class TestPictures { | |||
* Test read/write WMF | |||
*/ | |||
@Test | |||
public void testWMF() throws Exception { | |||
public void testWMF() throws IOException { | |||
HSLFSlideShow ppt = new HSLFSlideShow(); | |||
HSLFSlide slide = ppt.createSlide(); | |||
byte[] src_bytes = slTests.readFile("santa.wmf"); | |||
HSLFPictureData data = ppt.addPicture(src_bytes, PictureType.WMF); | |||
WMF.NativeHeader nHeader = new WMF.NativeHeader(src_bytes, 0); | |||
ImageHeaderWMF nHeader = new ImageHeaderWMF(src_bytes, 0); | |||
final int expWidth = 136, expHeight = 146; | |||
Dimension nDim = nHeader.getSize(); | |||
assertEquals(expWidth, nDim.getWidth(), 0); | |||
@@ -176,13 +180,13 @@ public final class TestPictures { | |||
* Test read/write EMF | |||
*/ | |||
@Test | |||
public void testEMF() throws Exception { | |||
public void testEMF() throws IOException { | |||
HSLFSlideShow ppt = new HSLFSlideShow(); | |||
HSLFSlide slide = ppt.createSlide(); | |||
byte[] src_bytes = slTests.readFile("wrench.emf"); | |||
HSLFPictureData data = ppt.addPicture(src_bytes, PictureType.EMF); | |||
EMF.NativeHeader nHeader = new EMF.NativeHeader(src_bytes, 0); | |||
ImageHeaderEMF nHeader = new ImageHeaderEMF(src_bytes, 0); | |||
final int expWidth = 190, expHeight = 115; | |||
Dimension nDim = nHeader.getSize(); | |||
assertEquals(expWidth, nDim.getWidth(), 0); | |||
@@ -233,7 +237,7 @@ public final class TestPictures { | |||
* Test read/write PNG | |||
*/ | |||
@Test | |||
public void testPNG() throws Exception { | |||
public void testPNG() throws IOException { | |||
HSLFSlideShow ppt = new HSLFSlideShow(); | |||
HSLFSlide slide = ppt.createSlide(); | |||
@@ -273,7 +277,7 @@ public final class TestPictures { | |||
* Test read/write JPEG | |||
*/ | |||
@Test | |||
public void testJPEG() throws Exception { | |||
public void testJPEG() throws IOException { | |||
HSLFSlideShow ppt = new HSLFSlideShow(); | |||
HSLFSlide slide = ppt.createSlide(); | |||
@@ -314,7 +318,7 @@ public final class TestPictures { | |||
* Test read/write DIB | |||
*/ | |||
@Test | |||
public void testDIB() throws Exception { | |||
public void testDIB() throws IOException { | |||
HSLFSlideShow ppt = new HSLFSlideShow(); | |||
HSLFSlide slide = ppt.createSlide(); | |||
@@ -354,7 +358,7 @@ public final class TestPictures { | |||
* Read pictures in different formats from a reference slide show | |||
*/ | |||
@Test | |||
public void testReadPictures() throws Exception { | |||
public void testReadPictures() throws IOException { | |||
byte[] src_bytes, ppt_bytes, b1, b2; | |||
HSLFPictureShape pict; | |||
@@ -417,6 +421,7 @@ public final class TestPictures { | |||
ppt_bytes = slTests.readFile("wrench.emf"); | |||
assertArrayEquals(src_bytes, ppt_bytes); | |||
ppt.close(); | |||
} | |||
/** | |||
@@ -424,7 +429,7 @@ public final class TestPictures { | |||
* crazy pictures of type 0, we do our best. | |||
*/ | |||
@Test | |||
public void testZeroPictureType() throws Exception { | |||
public void testZeroPictureType() throws IOException { | |||
HSLFSlideShowImpl hslf = new HSLFSlideShowImpl(slTests.openResourceAsStream("PictureTypeZero.ppt")); | |||
// Should still have 2 real pictures | |||
@@ -452,6 +457,8 @@ public final class TestPictures { | |||
pdata = pict.getPictureData(); | |||
assertTrue(pdata instanceof WMF); | |||
assertEquals(PictureType.WMF, pdata.getType()); | |||
ppt.close(); | |||
} | |||
/** | |||
@@ -460,7 +467,7 @@ public final class TestPictures { | |||
*/ | |||
@Test | |||
@Ignore | |||
public void testZeroPictureLength() throws Exception { | |||
public void testZeroPictureLength() throws IOException { | |||
// take the data from www instead of test directory | |||
URL url = new URL("http://www.cs.sfu.ca/~anoop/courses/CMPT-882-Fall-2002/chris.ppt"); | |||
HSLFSlideShowImpl hslf = new HSLFSlideShowImpl(url.openStream()); | |||
@@ -504,19 +511,21 @@ public final class TestPictures { | |||
assertEquals(streamSize, offset); | |||
assertEquals(3, ppt.getPictureData().size()); | |||
ppt.close(); | |||
} | |||
@Test | |||
public void testGetPictureName() throws Exception { | |||
public void testGetPictureName() throws IOException { | |||
HSLFSlideShow ppt = new HSLFSlideShow(slTests.openResourceAsStream("ppt_with_png.ppt")); | |||
HSLFSlide slide = ppt.getSlides().get(0); | |||
HSLFPictureShape p = (HSLFPictureShape)slide.getShapes().get(0); //the first slide contains JPEG | |||
assertEquals("test", p.getPictureName()); | |||
ppt.close(); | |||
} | |||
@Test | |||
public void testSetPictureName() throws Exception { | |||
public void testSetPictureName() throws IOException { | |||
HSLFSlideShow ppt = new HSLFSlideShow(); | |||
HSLFSlide slide = ppt.createSlide(); |