From 3553698c3a2579edb9108a39b0e399c30950f1f2 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Mon, 28 Nov 2016 20:51:02 +0000 Subject: [PATCH] #60427 - Creating pictures in PowerPoint slides requires scratchpad-jar for adding WMF images git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1771812 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/sl/image/ImageHeaderBitmap.java | 57 +++++++ .../apache/poi/sl/image/ImageHeaderEMF.java | 68 ++++++++ .../apache/poi/sl/image/ImageHeaderPICT.java | 103 ++++++++++++ .../apache/poi/sl/image/ImageHeaderWMF.java | 154 ++++++++++++++++++ .../poi/xslf/usermodel/XSLFPictureData.java | 35 +--- .../src/org/apache/poi/hslf/blip/EMF.java | 40 +---- .../src/org/apache/poi/hslf/blip/PICT.java | 81 +-------- .../src/org/apache/poi/hslf/blip/WMF.java | 130 +-------------- .../poi/hslf/usermodel/TestPictures.java | 37 +++-- 9 files changed, 424 insertions(+), 281 deletions(-) create mode 100644 src/java/org/apache/poi/sl/image/ImageHeaderBitmap.java create mode 100644 src/java/org/apache/poi/sl/image/ImageHeaderEMF.java create mode 100644 src/java/org/apache/poi/sl/image/ImageHeaderPICT.java create mode 100644 src/java/org/apache/poi/sl/image/ImageHeaderWMF.java diff --git a/src/java/org/apache/poi/sl/image/ImageHeaderBitmap.java b/src/java/org/apache/poi/sl/image/ImageHeaderBitmap.java new file mode 100644 index 0000000000..3ecc6e55c7 --- /dev/null +++ b/src/java/org/apache/poi/sl/image/ImageHeaderBitmap.java @@ -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; + } +} diff --git a/src/java/org/apache/poi/sl/image/ImageHeaderEMF.java b/src/java/org/apache/poi/sl/image/ImageHeaderEMF.java new file mode 100644 index 0000000000..7845c0ff9b --- /dev/null +++ b/src/java/org/apache/poi/sl/image/ImageHeaderEMF.java @@ -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; + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/sl/image/ImageHeaderPICT.java b/src/java/org/apache/poi/sl/image/ImageHeaderPICT.java new file mode 100644 index 0000000000..d994fb8ce3 --- /dev/null +++ b/src/java/org/apache/poi/sl/image/ImageHeaderPICT.java @@ -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; + } +} diff --git a/src/java/org/apache/poi/sl/image/ImageHeaderWMF.java b/src/java/org/apache/poi/sl/image/ImageHeaderWMF.java new file mode 100644 index 0000000000..b57b4d0099 --- /dev/null +++ b/src/java/org/apache/poi/sl/image/ImageHeaderWMF.java @@ -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. + * + */ +@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; + } +} diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java index 9f705e0e04..3405ef3725 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java @@ -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; } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/EMF.java b/src/scratchpad/src/org/apache/poi/hslf/blip/EMF.java index 4d7de5f75d..7eb285dfb7 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/blip/EMF.java +++ b/src/scratchpad/src/org/apache/poi/hslf/blip/EMF.java @@ -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; diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java b/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java index d68b4f7016..c79bade364 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java +++ b/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java @@ -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())); diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java b/src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java index 9dbc7f3bc3..78aa9f364f 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java +++ b/src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java @@ -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. - * - */ - @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; - } - } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java index 3afcfe5962..398603cb78 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java @@ -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(); -- 2.39.5