001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017: package org.apache.poi.hslf.blip;
018:
019: import org.apache.poi.util.LittleEndian;
020: import org.apache.poi.hslf.model.Picture;
021: import org.apache.poi.hslf.model.Shape;
022: import org.apache.poi.hslf.exceptions.HSLFException;
023:
024: import java.io.*;
025: import java.util.zip.InflaterInputStream;
026: import java.util.zip.DeflaterOutputStream;
027:
028: /**
029: * Represents a WMF (Windows Metafile) picture data.
030: *
031: * @author Yegor Kozlov
032: */
033: public class WMF extends Metafile {
034:
035: /**
036: * Extract compressed WMF data from a ppt
037: */
038: public byte[] getData() {
039: try {
040: byte[] rawdata = getRawData();
041:
042: ByteArrayOutputStream out = new ByteArrayOutputStream();
043: InputStream is = new ByteArrayInputStream(rawdata);
044: Header header = new Header();
045: header.read(rawdata, CHECKSUM_SIZE);
046: is.skip(header.getSize() + CHECKSUM_SIZE);
047:
048: AldusHeader aldus = new AldusHeader();
049: aldus.left = header.bounds.x;
050: aldus.top = header.bounds.y;
051: aldus.right = header.bounds.x + header.bounds.width;
052: aldus.bottom = header.bounds.y + header.bounds.height;
053: aldus.write(out);
054:
055: InflaterInputStream inflater = new InflaterInputStream(is);
056: byte[] chunk = new byte[4096];
057: int count;
058: while ((count = inflater.read(chunk)) >= 0) {
059: out.write(chunk, 0, count);
060: }
061: inflater.close();
062: return out.toByteArray();
063: } catch (IOException e) {
064: throw new HSLFException(e);
065: }
066: }
067:
068: public void setData(byte[] data) throws IOException {
069: int pos = 0;
070: AldusHeader aldus = new AldusHeader();
071: aldus.read(data, pos);
072: pos += aldus.getSize();
073:
074: byte[] compressed = compress(data, pos, data.length - pos);
075:
076: Header header = new Header();
077: header.wmfsize = data.length - aldus.getSize();
078: header.bounds = new java.awt.Rectangle((short) aldus.left,
079: (short) aldus.top, (short) aldus.right
080: - (short) aldus.left, (short) aldus.bottom
081: - (short) aldus.top);
082: //coefficiaent to translate from WMF dpi to 96pdi
083: int coeff = 96 * Shape.EMU_PER_POINT / aldus.inch;
084: header.size = new java.awt.Dimension(header.bounds.width
085: * coeff, header.bounds.height * coeff);
086: header.zipsize = compressed.length;
087:
088: byte[] checksum = getChecksum(data);
089: ByteArrayOutputStream out = new ByteArrayOutputStream();
090: out.write(checksum);
091: header.write(out);
092: out.write(compressed);
093:
094: setRawData(out.toByteArray());
095: }
096:
097: /**
098: * We are of type <code>Picture.WMF</code>
099: */
100: public int getType() {
101: return Picture.WMF;
102: }
103:
104: /**
105: * WMF signature is <code>0x2160</code>
106: */
107: public int getSignature() {
108: return 0x2160;
109: }
110:
111: /**
112: * Aldus Placeable Metafile header - 22 byte structure before WMF data.
113: * <ul>
114: * <li>int Key; Magic number (always 9AC6CDD7h)
115: * <li>short Handle; Metafile HANDLE number (always 0)
116: * <li>short Left; Left coordinate in metafile units
117: * <li>short Top; Top coordinate in metafile units
118: * <li>short Right; Right coordinate in metafile units
119: * <li>short Bottom; Bottom coordinate in metafile units
120: * <li>short Inch; Number of metafile units per inch
121: * <li>int Reserved; Reserved (always 0)
122: * <li>short Checksum; Checksum value for previous 10 shorts
123: * </ul>
124: */
125: public static class AldusHeader {
126: public static final int APMHEADER_KEY = 0x9AC6CDD7;
127:
128: public int handle;
129: public int left, top, right, bottom;
130: public int inch = 72; //default resolution is 72 dpi
131: public int reserved;
132: public int checksum;
133:
134: public void read(byte[] data, int offset) {
135: int pos = offset;
136: int key = LittleEndian.getInt(data, pos);
137: pos += LittleEndian.INT_SIZE; //header key
138: if (key != APMHEADER_KEY)
139: throw new HSLFException("Not a valid WMF file");
140:
141: handle = LittleEndian.getUShort(data, pos);
142: pos += LittleEndian.SHORT_SIZE;
143: left = LittleEndian.getUShort(data, pos);
144: pos += LittleEndian.SHORT_SIZE;
145: top = LittleEndian.getUShort(data, pos);
146: pos += LittleEndian.SHORT_SIZE;
147: right = LittleEndian.getUShort(data, pos);
148: pos += LittleEndian.SHORT_SIZE;
149: bottom = LittleEndian.getUShort(data, pos);
150: pos += LittleEndian.SHORT_SIZE;
151:
152: inch = LittleEndian.getUShort(data, pos);
153: pos += LittleEndian.SHORT_SIZE;
154: reserved = LittleEndian.getInt(data, pos);
155: pos += LittleEndian.INT_SIZE;
156:
157: checksum = LittleEndian.getShort(data, pos);
158: pos += LittleEndian.SHORT_SIZE;
159: if (checksum != getChecksum())
160: throw new HSLFException(
161: "WMF checksum does not match the header data");
162: }
163:
164: /**
165: * Returns a checksum value for the previous 10 shorts in the header.
166: * The checksum is calculated by XORing each short value to an initial value of 0:
167: */
168: public int getChecksum() {
169: int checksum = 0;
170: checksum ^= (APMHEADER_KEY & 0x0000FFFF);
171: checksum ^= ((APMHEADER_KEY & 0xFFFF0000) >> 16);
172: checksum ^= left;
173: checksum ^= top;
174: checksum ^= right;
175: checksum ^= bottom;
176: checksum ^= inch;
177: return checksum;
178: }
179:
180: public void write(OutputStream out) throws IOException {
181: byte[] header = new byte[22];
182: int pos = 0;
183: LittleEndian.putInt(header, pos, APMHEADER_KEY);
184: pos += LittleEndian.INT_SIZE; //header key
185: LittleEndian.putUShort(header, pos, 0);
186: pos += LittleEndian.SHORT_SIZE; //hmf
187: LittleEndian.putUShort(header, pos, left);
188: pos += LittleEndian.SHORT_SIZE; //left
189: LittleEndian.putUShort(header, pos, top);
190: pos += LittleEndian.SHORT_SIZE; //top
191: LittleEndian.putUShort(header, pos, right);
192: pos += LittleEndian.SHORT_SIZE; //right
193: LittleEndian.putUShort(header, pos, bottom);
194: pos += LittleEndian.SHORT_SIZE; //bottom
195: LittleEndian.putUShort(header, pos, inch);
196: pos += LittleEndian.SHORT_SIZE; //inch
197: LittleEndian.putInt(header, pos, 0);
198: pos += LittleEndian.INT_SIZE; //reserved
199:
200: checksum = getChecksum();
201: LittleEndian.putUShort(header, pos, checksum);
202:
203: out.write(header);
204: }
205:
206: public int getSize() {
207: return 22;
208: }
209: }
210:
211: }
|