mirror of https://github.com/apache/poi.git
Bug 60570 - Add rudimentary EMF read-only capability
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1779493 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
60f1ed67d4
commit
5750c85be9
|
@ -251,4 +251,27 @@ public final class IOUtils {
|
|||
exc );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips bytes from a stream. Returns -1L if EOF was hit before
|
||||
* the end of the stream.
|
||||
*
|
||||
* @param in inputstream
|
||||
* @param len length to skip
|
||||
* @return number of bytes skipped
|
||||
* @throws IOException on IOException
|
||||
*/
|
||||
public static long skipFully(InputStream in, long len) throws IOException {
|
||||
int total = 0;
|
||||
while (true) {
|
||||
long got = in.skip(len-total);
|
||||
if (got < 0) {
|
||||
return -1L;
|
||||
}
|
||||
total += got;
|
||||
if (total == len) {
|
||||
return total;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/* ====================================================================
|
||||
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.hemf.extractor;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.poi.hemf.record.HemfHeader;
|
||||
import org.apache.poi.hemf.record.HemfRecord;
|
||||
import org.apache.poi.hemf.record.HemfRecordType;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
import org.apache.poi.util.RecordFormatException;
|
||||
|
||||
/**
|
||||
* Read-only EMF extractor. Lots remain
|
||||
*/
|
||||
@Internal
|
||||
public class HemfExtractor implements Iterable<HemfRecord> {
|
||||
|
||||
private HemfHeader header;
|
||||
private final LittleEndianInputStream stream;
|
||||
|
||||
public HemfExtractor(InputStream is) throws IOException {
|
||||
stream = new LittleEndianInputStream(is);
|
||||
header = new HemfHeader();
|
||||
long recordId = stream.readUInt();
|
||||
long recordSize = stream.readUInt();
|
||||
|
||||
header = new HemfHeader();
|
||||
header.init(stream, recordId, recordSize-8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<HemfRecord> iterator() {
|
||||
return new HemfRecordIterator();
|
||||
}
|
||||
|
||||
public HemfHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
private class HemfRecordIterator implements Iterator<HemfRecord> {
|
||||
|
||||
private HemfRecord currentRecord = null;
|
||||
|
||||
HemfRecordIterator() {
|
||||
//queue the first non-header record
|
||||
currentRecord = _next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return currentRecord != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HemfRecord next() {
|
||||
HemfRecord toReturn = currentRecord;
|
||||
currentRecord = _next();
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
private HemfRecord _next() {
|
||||
if (currentRecord != null && currentRecord.getRecordType().equals(HemfRecordType.eof)) {
|
||||
return null;
|
||||
}
|
||||
long recordId = stream.readUInt();
|
||||
long recordSize = stream.readUInt();
|
||||
|
||||
HemfRecord record = null;
|
||||
HemfRecordType type = HemfRecordType.getById(recordId);
|
||||
if (type == null) {
|
||||
throw new RuntimeException("Undefined record of type:"+recordId);
|
||||
}
|
||||
try {
|
||||
record = type.clazz.newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
try {
|
||||
record.init(stream, recordId, recordSize-8);
|
||||
} catch (IOException e) {
|
||||
throw new RecordFormatException(e);
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Remove not supported");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/* ====================================================================
|
||||
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.hemf.hemfplus.record;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
@Internal
|
||||
public class HemfPlusHeader implements HemfPlusRecord {
|
||||
|
||||
private int flags;
|
||||
private long version; //hack for now; replace with EmfPlusGraphicsVersion object
|
||||
private long emfPlusFlags;
|
||||
private long logicalDpiX;
|
||||
private long logicalDpiY;
|
||||
|
||||
@Override
|
||||
public HemfPlusRecordType getRecordType() {
|
||||
return HemfPlusRecordType.header;
|
||||
}
|
||||
|
||||
public int getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(byte[] dataBytes, int recordId, int flags) throws IOException {
|
||||
//assert record id == header
|
||||
this.flags = flags;
|
||||
int offset = 0;
|
||||
this.version = LittleEndian.getUInt(dataBytes, offset); offset += LittleEndian.INT_SIZE;
|
||||
this.emfPlusFlags = LittleEndian.getUInt(dataBytes, offset); offset += LittleEndian.INT_SIZE;
|
||||
this.logicalDpiX = LittleEndian.getUInt(dataBytes, offset); offset += LittleEndian.INT_SIZE;
|
||||
this.logicalDpiY = LittleEndian.getUInt(dataBytes, offset);
|
||||
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public long getEmfPlusFlags() {
|
||||
return emfPlusFlags;
|
||||
}
|
||||
|
||||
public long getLogicalDpiX() {
|
||||
return logicalDpiX;
|
||||
}
|
||||
|
||||
public long getLogicalDpiY() {
|
||||
return logicalDpiY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HemfPlusHeader{" +
|
||||
"flags=" + flags +
|
||||
", version=" + version +
|
||||
", emfPlusFlags=" + emfPlusFlags +
|
||||
", logicalDpiX=" + logicalDpiX +
|
||||
", logicalDpiY=" + logicalDpiY +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/* ====================================================================
|
||||
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.hemf.hemfplus.record;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
|
||||
@Internal
|
||||
public interface HemfPlusRecord {
|
||||
|
||||
HemfPlusRecordType getRecordType();
|
||||
|
||||
int getFlags();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dataBytes these are the bytes that start after the id, flags, record size
|
||||
* and go to the end of the record; they do not include any required padding
|
||||
* at the end.
|
||||
* @param recordId record type id
|
||||
* @param flags flags
|
||||
* @return
|
||||
* @throws IOException, RecordFormatException
|
||||
*/
|
||||
void init(byte[] dataBytes, int recordId, int flags) throws IOException;
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/* ====================================================================
|
||||
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.hemf.hemfplus.record;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
|
||||
@Internal
|
||||
public enum HemfPlusRecordType {
|
||||
header(0x4001, HemfPlusHeader.class),
|
||||
endOfFile(0x4002, UnimplementedHemfPlusRecord.class),
|
||||
comment(0x4003, UnimplementedHemfPlusRecord.class),
|
||||
getDC(0x4004, UnimplementedHemfPlusRecord.class),
|
||||
multiFormatStart(0x4005, UnimplementedHemfPlusRecord.class),
|
||||
multiFormatSection(0x4006, UnimplementedHemfPlusRecord.class),
|
||||
multiFormatEnd(0x4007, UnimplementedHemfPlusRecord.class),
|
||||
object(0x4008, UnimplementedHemfPlusRecord.class),
|
||||
clear(0x4009, UnimplementedHemfPlusRecord.class),
|
||||
fillRects(0x400A, UnimplementedHemfPlusRecord.class),
|
||||
drawRects(0x400B, UnimplementedHemfPlusRecord.class),
|
||||
fillPolygon(0x400C, UnimplementedHemfPlusRecord.class),
|
||||
drawLines(0x400D, UnimplementedHemfPlusRecord.class),
|
||||
fillEllipse(0x400E, UnimplementedHemfPlusRecord.class),
|
||||
drawEllipse(0x400F, UnimplementedHemfPlusRecord.class),
|
||||
fillPie(0x4010, UnimplementedHemfPlusRecord.class),
|
||||
drawPie(0x4011, UnimplementedHemfPlusRecord.class),
|
||||
drawArc(0x4012, UnimplementedHemfPlusRecord.class),
|
||||
fillRegion(0x4013, UnimplementedHemfPlusRecord.class),
|
||||
fillPath(0x4014, UnimplementedHemfPlusRecord.class),
|
||||
drawPath(0x4015, UnimplementedHemfPlusRecord.class),
|
||||
fillClosedCurve(0x4016, UnimplementedHemfPlusRecord.class),
|
||||
drawClosedCurve(0x4017, UnimplementedHemfPlusRecord.class),
|
||||
drawCurve(0x4018, UnimplementedHemfPlusRecord.class),
|
||||
drawBeziers(0x4019, UnimplementedHemfPlusRecord.class),
|
||||
drawImage(0x401A, UnimplementedHemfPlusRecord.class),
|
||||
drawImagePoints(0x401B, UnimplementedHemfPlusRecord.class),
|
||||
drawString(0x401C, UnimplementedHemfPlusRecord.class),
|
||||
setRenderingOrigin(0x401D, UnimplementedHemfPlusRecord.class),
|
||||
setAntiAliasMode(0x401E, UnimplementedHemfPlusRecord.class),
|
||||
setTextRenderingHint(0x401F, UnimplementedHemfPlusRecord.class),
|
||||
setTextContrast(0x4020, UnimplementedHemfPlusRecord.class),
|
||||
setInterpolationMode(0x4021, UnimplementedHemfPlusRecord.class),
|
||||
setPixelOffsetMode(0x4022, UnimplementedHemfPlusRecord.class),
|
||||
setComositingMode(0x4023, UnimplementedHemfPlusRecord.class),
|
||||
setCompositingQuality(0x4024, UnimplementedHemfPlusRecord.class),
|
||||
save(0x4025, UnimplementedHemfPlusRecord.class),
|
||||
restore(0x4026, UnimplementedHemfPlusRecord.class),
|
||||
beginContainer(0x4027, UnimplementedHemfPlusRecord.class),
|
||||
beginContainerNoParams(0x428, UnimplementedHemfPlusRecord.class),
|
||||
endContainer(0x4029, UnimplementedHemfPlusRecord.class),
|
||||
setWorldTransform(0x402A, UnimplementedHemfPlusRecord.class),
|
||||
resetWorldTransform(0x402B, UnimplementedHemfPlusRecord.class),
|
||||
multiplyWorldTransform(0x402C, UnimplementedHemfPlusRecord.class),
|
||||
translateWorldTransform(0x402D, UnimplementedHemfPlusRecord.class),
|
||||
scaleWorldTransform(0x402E, UnimplementedHemfPlusRecord.class),
|
||||
rotateWorldTransform(0x402F, UnimplementedHemfPlusRecord.class),
|
||||
setPageTransform(0x4030, UnimplementedHemfPlusRecord.class),
|
||||
resetClip(0x4031, UnimplementedHemfPlusRecord.class),
|
||||
setClipRect(0x4032, UnimplementedHemfPlusRecord.class),
|
||||
setClipRegion(0x4033, UnimplementedHemfPlusRecord.class),
|
||||
setClipPath(0x4034, UnimplementedHemfPlusRecord.class),
|
||||
offsetClip(0x4035, UnimplementedHemfPlusRecord.class),
|
||||
drawDriverstring(0x4036, UnimplementedHemfPlusRecord.class),
|
||||
strokeFillPath(0x4037, UnimplementedHemfPlusRecord.class),
|
||||
serializableObject(0x4038, UnimplementedHemfPlusRecord.class),
|
||||
setTSGraphics(0x4039, UnimplementedHemfPlusRecord.class),
|
||||
setTSClip(0x403A, UnimplementedHemfPlusRecord.class);
|
||||
|
||||
public final long id;
|
||||
public final Class<? extends HemfPlusRecord> clazz;
|
||||
|
||||
HemfPlusRecordType(long id, Class<? extends HemfPlusRecord> clazz) {
|
||||
this.id = id;
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public static HemfPlusRecordType getById(long id) {
|
||||
for (HemfPlusRecordType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/* ====================================================================
|
||||
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.hemf.hemfplus.record;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
|
||||
@Internal
|
||||
public class UnimplementedHemfPlusRecord implements HemfPlusRecord {
|
||||
|
||||
private int recordId;
|
||||
private int flags;
|
||||
private byte[] recordBytes;
|
||||
|
||||
@Override
|
||||
public HemfPlusRecordType getRecordType() {
|
||||
return HemfPlusRecordType.getById(recordId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(byte[] recordBytes, int recordId, int flags) throws IOException {
|
||||
this.recordId = recordId;
|
||||
this.flags = flags;
|
||||
this.recordBytes = recordBytes;
|
||||
}
|
||||
|
||||
public byte[] getRecordBytes() {
|
||||
//should probably defensively return a copy.
|
||||
return recordBytes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
|
||||
/**
|
||||
* Syntactic utility to allow for four different
|
||||
* comment classes
|
||||
*/
|
||||
@Internal
|
||||
public abstract class AbstractHemfComment {
|
||||
|
||||
private final byte[] rawBytes;
|
||||
|
||||
public AbstractHemfComment(byte[] rawBytes) {
|
||||
this.rawBytes = rawBytes;
|
||||
}
|
||||
|
||||
public byte[] getRawBytes() {
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
|
||||
/**
|
||||
* Contains arbitrary data
|
||||
*/
|
||||
@Internal
|
||||
public class HemfComment extends AbstractHemfComment {
|
||||
|
||||
public HemfComment(byte[] rawBytes) {
|
||||
super(rawBytes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hemf.hemfplus.record.HemfPlusRecord;
|
||||
import org.apache.poi.hemf.hemfplus.record.HemfPlusRecordType;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.RecordFormatException;
|
||||
|
||||
/**
|
||||
* An HemfCommentEMFPlus may contain one or more EMFPlus records
|
||||
*/
|
||||
@Internal
|
||||
public class HemfCommentEMFPlus extends AbstractHemfComment {
|
||||
|
||||
long dataSize;
|
||||
public HemfCommentEMFPlus(byte[] rawBytes) {
|
||||
//these rawBytes contain only the EMFPlusRecord(s?)
|
||||
//the EmfComment type, size, datasize and comment identifier have all been stripped.
|
||||
//The EmfPlus type, flags, size, data size should start at rawBytes[0]
|
||||
super(rawBytes);
|
||||
|
||||
}
|
||||
|
||||
public List<HemfPlusRecord> getRecords() {
|
||||
return HemfPlusParser.parse(getRawBytes());
|
||||
}
|
||||
|
||||
private static class HemfPlusParser {
|
||||
|
||||
public static List<HemfPlusRecord> parse(byte[] bytes) {
|
||||
List<HemfPlusRecord> records = new ArrayList<HemfPlusRecord>();
|
||||
int offset = 0;
|
||||
while (offset < bytes.length) {
|
||||
if (offset + 12 > bytes.length) {
|
||||
//if header will go beyond bytes, stop now
|
||||
//TODO: log or throw
|
||||
break;
|
||||
}
|
||||
int type = LittleEndian.getUShort(bytes, offset); offset += LittleEndian.SHORT_SIZE;
|
||||
int flags = LittleEndian.getUShort(bytes, offset); offset += LittleEndian.SHORT_SIZE;
|
||||
long sizeLong = LittleEndian.getUInt(bytes, offset); offset += LittleEndian.INT_SIZE;
|
||||
if (sizeLong >= Integer.MAX_VALUE) {
|
||||
throw new RecordFormatException("size of emf record >= Integer.MAX_VALUE");
|
||||
}
|
||||
int size = (int)sizeLong;
|
||||
long dataSizeLong = LittleEndian.getUInt(bytes, offset); offset += LittleEndian.INT_SIZE;
|
||||
if (dataSizeLong >= Integer.MAX_VALUE) {
|
||||
throw new RuntimeException("data size of emfplus record cannot be >= Integer.MAX_VALUE");
|
||||
}
|
||||
int dataSize = (int)dataSizeLong;
|
||||
if (dataSize + offset > bytes.length) {
|
||||
//TODO: log or throw?
|
||||
break;
|
||||
}
|
||||
HemfPlusRecord record = buildRecord(type, flags, dataSize, offset, bytes);
|
||||
records.add(record);
|
||||
offset += dataSize;
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
private static HemfPlusRecord buildRecord(int recordId, int flags, int size, int offset, byte[] bytes) {
|
||||
HemfPlusRecord record = null;
|
||||
HemfPlusRecordType type = HemfPlusRecordType.getById(recordId);
|
||||
if (type == null) {
|
||||
throw new RuntimeException("Undefined record of type:"+recordId);
|
||||
}
|
||||
try {
|
||||
record = type.clazz.newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
byte[] dataBytes = new byte[size];
|
||||
System.arraycopy(bytes, offset, dataBytes, 0, size);
|
||||
try {
|
||||
record.init(dataBytes, recordId, flags);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return record;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
|
||||
/**
|
||||
* Not yet implemented
|
||||
*/
|
||||
@Internal
|
||||
public class HemfCommentEMFSpool extends AbstractHemfComment {
|
||||
|
||||
public HemfCommentEMFSpool(byte[] rawBytes) {
|
||||
super(rawBytes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.RecordFormatException;
|
||||
|
||||
/**
|
||||
* Container class for four subtypes of HemfCommentPublic: BeginGroup, EndGroup, MultiFormats
|
||||
* and Windows Metafile.
|
||||
*/
|
||||
@Internal
|
||||
public class HemfCommentPublic {
|
||||
|
||||
/**
|
||||
* Stub, to be implemented
|
||||
*/
|
||||
public static class BeginGroup extends AbstractHemfComment {
|
||||
|
||||
public BeginGroup(byte[] rawBytes) {
|
||||
super(rawBytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub, to be implemented
|
||||
*/
|
||||
public static class EndGroup extends AbstractHemfComment {
|
||||
|
||||
public EndGroup(byte[] rawBytes) {
|
||||
super(rawBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultiFormats extends AbstractHemfComment {
|
||||
|
||||
public MultiFormats(byte[] rawBytes) {
|
||||
super(rawBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return a list of HemfMultFormatsData
|
||||
*/
|
||||
public List<HemfMultiFormatsData> getData() {
|
||||
|
||||
byte[] rawBytes = getRawBytes();
|
||||
//note that raw bytes includes the public comment identifier
|
||||
int currentOffset = 4 + 16;//4 public comment identifier, 16 for outputrect
|
||||
long countFormats = LittleEndian.getUInt(rawBytes, currentOffset);
|
||||
currentOffset += LittleEndian.INT_SIZE;
|
||||
List<EmrFormat> emrFormatList = new ArrayList<EmrFormat>();
|
||||
for (long i = 0; i < countFormats; i++) {
|
||||
emrFormatList.add(new EmrFormat(rawBytes, currentOffset));
|
||||
currentOffset += 4 * LittleEndian.INT_SIZE;
|
||||
}
|
||||
List<HemfMultiFormatsData> list = new ArrayList<HemfMultiFormatsData>();
|
||||
for (EmrFormat emrFormat : emrFormatList) {
|
||||
byte[] data = new byte[emrFormat.size];
|
||||
System.arraycopy(rawBytes, emrFormat.offset-4, data, 0, emrFormat.size);
|
||||
list.add(new HemfMultiFormatsData(emrFormat.signature, emrFormat.version, data));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private class EmrFormat {
|
||||
long signature;
|
||||
long version;
|
||||
int size;
|
||||
int offset;
|
||||
|
||||
public EmrFormat(byte[] rawBytes, int currentOffset) {
|
||||
signature = LittleEndian.getUInt(rawBytes, currentOffset); currentOffset += LittleEndian.INT_SIZE;
|
||||
version = LittleEndian.getUInt(rawBytes, currentOffset); currentOffset += LittleEndian.INT_SIZE;
|
||||
//spec says this must be a 32bit "aligned" typo for "signed"?
|
||||
//realistically, this has to be an int...
|
||||
size = LittleEndian.getInt(rawBytes, currentOffset); currentOffset += LittleEndian.INT_SIZE;
|
||||
//y, can be long, but realistically?
|
||||
offset = LittleEndian.getInt(rawBytes, currentOffset); currentOffset += LittleEndian.INT_SIZE;
|
||||
if (size < 0) {
|
||||
throw new RecordFormatException("size for emrformat must be > 0");
|
||||
}
|
||||
if (offset < 0) {
|
||||
throw new RecordFormatException("offset for emrformat must be > 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub, to be implemented
|
||||
*/
|
||||
public static class WindowsMetafile extends AbstractHemfComment {
|
||||
|
||||
private final byte[] wmfBytes;
|
||||
public WindowsMetafile(byte[] rawBytes) {
|
||||
super(rawBytes);
|
||||
int offset = LittleEndian.INT_SIZE;//public comment identifier
|
||||
int version = LittleEndian.getUShort(rawBytes, offset); offset += LittleEndian.SHORT_SIZE;
|
||||
int reserved = LittleEndian.getUShort(rawBytes, offset); offset += LittleEndian.SHORT_SIZE;
|
||||
offset += LittleEndian.INT_SIZE; //checksum
|
||||
offset += LittleEndian.INT_SIZE; //flags
|
||||
long winMetafileSizeLong = LittleEndian.getUInt(rawBytes, offset); offset += LittleEndian.INT_SIZE;
|
||||
if (winMetafileSizeLong == 0L) {
|
||||
wmfBytes = new byte[0];
|
||||
return;
|
||||
}
|
||||
if (winMetafileSizeLong > Integer.MAX_VALUE) {
|
||||
throw new RecordFormatException("Metafile record length can't be > Integer.MAX_VALUE");
|
||||
}
|
||||
int winMetafileSize = (int)winMetafileSizeLong;
|
||||
wmfBytes = new byte[winMetafileSize];
|
||||
System.arraycopy(rawBytes, offset, wmfBytes, 0, winMetafileSize);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return an InputStream for the embedded WMF file
|
||||
*/
|
||||
public InputStream getWmfInputStream() {
|
||||
return new ByteArrayInputStream(wmfBytes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This encapulates a single record stored within
|
||||
* a HemfCommentPublic.MultiFormats record.
|
||||
*/
|
||||
public static class HemfMultiFormatsData {
|
||||
|
||||
long signature;
|
||||
long version;
|
||||
byte[] data;
|
||||
|
||||
public HemfMultiFormatsData(long signature, long version, byte[] data) {
|
||||
this.signature = signature;
|
||||
this.version = version;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public long getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
import org.apache.poi.util.RecordFormatException;
|
||||
|
||||
/**
|
||||
* This is the outer comment record that is recognized
|
||||
* by the initial parse by {@link HemfRecordType#comment}.
|
||||
* However, there are four types of comment: EMR_COMMENT,
|
||||
* EMR_COMMENT_EMFPLUS, EMR_COMMENT_EMFSPOOL, and EMF_COMMENT_PUBLIC.
|
||||
* To get the underlying comment, call {@link #getComment()}.
|
||||
*
|
||||
*/
|
||||
@Internal
|
||||
public class HemfCommentRecord implements HemfRecord {
|
||||
|
||||
public final static long COMMENT_EMFSPOOL = 0x00000000;
|
||||
public final static long COMMENT_EMFPLUS = 0x2B464D45;
|
||||
public final static long COMMENT_PUBLIC = 0x43494447;
|
||||
|
||||
|
||||
private AbstractHemfComment comment;
|
||||
@Override
|
||||
public HemfRecordType getRecordType() {
|
||||
return HemfRecordType.comment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long recordId, long recordSize) throws IOException {
|
||||
long dataSize = leis.readUInt(); recordSize -= LittleEndian.INT_SIZE;
|
||||
|
||||
byte[] optionalCommentIndentifierBuffer = new byte[4];
|
||||
leis.readFully(optionalCommentIndentifierBuffer);
|
||||
dataSize = dataSize-LittleEndian.INT_SIZE; //size minus the first int which could be a comment identifier
|
||||
recordSize -= LittleEndian.INT_SIZE;
|
||||
long optionalCommentIdentifier = LittleEndian.getInt(optionalCommentIndentifierBuffer) & 0x00FFFFFFFFL;
|
||||
if (optionalCommentIdentifier == COMMENT_EMFSPOOL) {
|
||||
comment = new HemfCommentEMFSpool(readToByteArray(leis, dataSize, recordSize));
|
||||
} else if (optionalCommentIdentifier == COMMENT_EMFPLUS) {
|
||||
comment = new HemfCommentEMFPlus(readToByteArray(leis, dataSize, recordSize));
|
||||
} else if (optionalCommentIdentifier == COMMENT_PUBLIC) {
|
||||
comment = CommentPublicParser.parse(readToByteArray(leis, dataSize, recordSize));
|
||||
} else {
|
||||
comment = new HemfComment(readToByteArray(optionalCommentIndentifierBuffer, leis, dataSize, recordSize));
|
||||
}
|
||||
|
||||
return recordSize;
|
||||
}
|
||||
|
||||
//this prepends the initial "int" which turned out NOT to be
|
||||
//a signifier of emfplus, spool, public.
|
||||
private byte[] readToByteArray(byte[] initialBytes, LittleEndianInputStream leis,
|
||||
long remainingDataSize, long remainingRecordSize) throws IOException {
|
||||
if (remainingDataSize > Integer.MAX_VALUE) {
|
||||
throw new RecordFormatException("Data size can't be > Integer.MAX_VALUE");
|
||||
}
|
||||
|
||||
if (remainingRecordSize > Integer.MAX_VALUE) {
|
||||
throw new RecordFormatException("Record size can't be > Integer.MAX_VALUE");
|
||||
}
|
||||
if (remainingRecordSize == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
int dataSize = (int)remainingDataSize;
|
||||
int recordSize = (int)remainingRecordSize;
|
||||
byte[] arr = new byte[dataSize+initialBytes.length];
|
||||
System.arraycopy(initialBytes,0,arr, 0, initialBytes.length);
|
||||
IOUtils.readFully(leis, arr, initialBytes.length, dataSize);
|
||||
IOUtils.skipFully(leis, recordSize-dataSize);
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
private byte[] readToByteArray(LittleEndianInputStream leis, long dataSize, long recordSize) throws IOException {
|
||||
assert dataSize < Integer.MAX_VALUE;
|
||||
|
||||
if (recordSize == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
byte[] arr = new byte[(int)dataSize];
|
||||
IOUtils.readFully(leis, arr);
|
||||
IOUtils.skipFully(leis, recordSize-dataSize);
|
||||
return arr;
|
||||
}
|
||||
|
||||
public AbstractHemfComment getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
private static class CommentPublicParser {
|
||||
private static final long WINDOWS_METAFILE = 0x80000001L; //wmf
|
||||
private static final long BEGINGROUP = 0x00000002; //beginning of a group of drawing records
|
||||
private static final long ENDGROUP = 0x00000003; //end of a group of drawing records
|
||||
private static final long MULTIFORMATS = 0x40000004; //allows multiple definitions of an image, including encapsulated postscript
|
||||
private static final long UNICODE_STRING = 0x00000040; //reserved. must not be used
|
||||
private static final long UNICODE_END = 0x00000080; //reserved, must not be used
|
||||
|
||||
private static AbstractHemfComment parse(byte[] bytes) {
|
||||
long publicCommentIdentifier = LittleEndian.getUInt(bytes, 0);
|
||||
if (publicCommentIdentifier == WINDOWS_METAFILE) {
|
||||
return new HemfCommentPublic.WindowsMetafile(bytes);
|
||||
} else if (publicCommentIdentifier == BEGINGROUP) {
|
||||
return new HemfCommentPublic.BeginGroup(bytes);
|
||||
} else if (publicCommentIdentifier == ENDGROUP) {
|
||||
return new HemfCommentPublic.EndGroup(bytes);
|
||||
} else if (publicCommentIdentifier == MULTIFORMATS) {
|
||||
return new HemfCommentPublic.MultiFormats(bytes);
|
||||
} else if (publicCommentIdentifier == UNICODE_STRING || publicCommentIdentifier == UNICODE_END) {
|
||||
throw new RuntimeException("UNICODE_STRING/UNICODE_END values are reserved in CommentPublic records");
|
||||
}
|
||||
throw new RuntimeException("Unrecognized public comment type:" +publicCommentIdentifier + " ; " + WINDOWS_METAFILE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
|
||||
/**
|
||||
* Extracts the full header from EMF files.
|
||||
* @see org.apache.poi.sl.image.ImageHeaderEMF
|
||||
*/
|
||||
@Internal
|
||||
public class HemfHeader implements HemfRecord {
|
||||
|
||||
private Rectangle boundsRectangle;
|
||||
private Rectangle frameRectangle;
|
||||
private long bytes;
|
||||
private long records;
|
||||
private int handles;
|
||||
private long nDescription;
|
||||
private long offDescription;
|
||||
private long nPalEntries;
|
||||
private boolean hasExtension1;
|
||||
private long cbPixelFormat;
|
||||
private long offPixelFormat;
|
||||
private long bOpenGL;
|
||||
private boolean hasExtension2;
|
||||
private long micrometersX;
|
||||
private long micrometersY;
|
||||
|
||||
public Rectangle getBoundsRectangle() {
|
||||
return boundsRectangle;
|
||||
}
|
||||
|
||||
public Rectangle getFrameRectangle() {
|
||||
return frameRectangle;
|
||||
}
|
||||
|
||||
public long getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public long getRecords() {
|
||||
return records;
|
||||
}
|
||||
|
||||
public int getHandles() {
|
||||
return handles;
|
||||
}
|
||||
|
||||
public long getnDescription() {
|
||||
return nDescription;
|
||||
}
|
||||
|
||||
public long getOffDescription() {
|
||||
return offDescription;
|
||||
}
|
||||
|
||||
public long getnPalEntries() {
|
||||
return nPalEntries;
|
||||
}
|
||||
|
||||
public boolean isHasExtension1() {
|
||||
return hasExtension1;
|
||||
}
|
||||
|
||||
public long getCbPixelFormat() {
|
||||
return cbPixelFormat;
|
||||
}
|
||||
|
||||
public long getOffPixelFormat() {
|
||||
return offPixelFormat;
|
||||
}
|
||||
|
||||
public long getbOpenGL() {
|
||||
return bOpenGL;
|
||||
}
|
||||
|
||||
public boolean isHasExtension2() {
|
||||
return hasExtension2;
|
||||
}
|
||||
|
||||
public long getMicrometersX() {
|
||||
return micrometersX;
|
||||
}
|
||||
|
||||
public long getMicrometersY() {
|
||||
return micrometersY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HemfHeader{" +
|
||||
"boundsRectangle=" + boundsRectangle +
|
||||
", frameRectangle=" + frameRectangle +
|
||||
", bytes=" + bytes +
|
||||
", records=" + records +
|
||||
", handles=" + handles +
|
||||
", nDescription=" + nDescription +
|
||||
", offDescription=" + offDescription +
|
||||
", nPalEntries=" + nPalEntries +
|
||||
", hasExtension1=" + hasExtension1 +
|
||||
", cbPixelFormat=" + cbPixelFormat +
|
||||
", offPixelFormat=" + offPixelFormat +
|
||||
", bOpenGL=" + bOpenGL +
|
||||
", hasExtension2=" + hasExtension2 +
|
||||
", micrometersX=" + micrometersX +
|
||||
", micrometersY=" + micrometersY +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public HemfRecordType getRecordType() {
|
||||
return HemfRecordType.header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long recordId, long recordSize) throws IOException {
|
||||
if (recordId != 1L) {
|
||||
throw new IOException("Not a valid EMF header. Record type:"+recordId);
|
||||
}
|
||||
//read the record--id and size (2 bytes) have already been read
|
||||
byte[] data = new byte[(int)recordSize];
|
||||
IOUtils.readFully(leis, data);
|
||||
|
||||
int offset = 0;
|
||||
|
||||
//bounds
|
||||
int boundsLeft = LittleEndian.getInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
int boundsTop = LittleEndian.getInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
int boundsRight = LittleEndian.getInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
int boundsBottom = LittleEndian.getInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
boundsRectangle = new Rectangle(boundsLeft, boundsTop,
|
||||
boundsRight - boundsLeft, boundsBottom - boundsTop);
|
||||
|
||||
int frameLeft = LittleEndian.getInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
int frameTop = LittleEndian.getInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
int frameRight = LittleEndian.getInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
int frameBottom = LittleEndian.getInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
frameRectangle = new Rectangle(frameLeft, frameTop,
|
||||
frameRight - frameLeft, frameBottom - frameTop);
|
||||
|
||||
long recordSignature = LittleEndian.getInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
if (recordSignature != 0x464D4520) {
|
||||
throw new IOException("bad record signature: " + recordSignature);
|
||||
}
|
||||
|
||||
long version = LittleEndian.getInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
//According to the spec, MSOffice doesn't pay attention to this value.
|
||||
//It _should_ be 0x00010000
|
||||
bytes = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
records = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
handles = LittleEndian.getUShort(data, offset);offset += LittleEndian.SHORT_SIZE;
|
||||
offset += LittleEndian.SHORT_SIZE;//reserved
|
||||
nDescription = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
offDescription = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
nPalEntries = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
|
||||
//should be skips
|
||||
offset += 8;//device
|
||||
offset += 8;//millimeters
|
||||
|
||||
|
||||
if (recordSize+8 >= 100) {
|
||||
hasExtension1 = true;
|
||||
cbPixelFormat = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
offPixelFormat = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
bOpenGL= LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
}
|
||||
|
||||
if (recordSize+8 >= 108) {
|
||||
hasExtension2 = true;
|
||||
micrometersX = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
micrometersY = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE;
|
||||
}
|
||||
return recordSize;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
|
||||
@Internal
|
||||
public interface HemfRecord {
|
||||
|
||||
HemfRecordType getRecordType();
|
||||
|
||||
/**
|
||||
* Init record from stream
|
||||
*
|
||||
* @param leis the little endian input stream
|
||||
* @return count of processed bytes
|
||||
* @throws IOException
|
||||
*/
|
||||
long init(LittleEndianInputStream leis, long recordId, long recordSize) throws IOException;
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
import org.apache.poi.util.Internal;
|
||||
|
||||
@Internal
|
||||
public enum HemfRecordType {
|
||||
|
||||
header(0x00000001, UnimplementedHemfRecord.class),
|
||||
polybeizer(0x00000002, UnimplementedHemfRecord.class),
|
||||
polygon(0x00000003, UnimplementedHemfRecord.class),
|
||||
polyline(0x00000004, UnimplementedHemfRecord.class),
|
||||
polybezierto(0x00000005, UnimplementedHemfRecord.class),
|
||||
polylineto(0x00000006, UnimplementedHemfRecord.class),
|
||||
polypolyline(0x00000007, UnimplementedHemfRecord.class),
|
||||
polypolygon(0x00000008, UnimplementedHemfRecord.class),
|
||||
setwindowextex(0x00000009, UnimplementedHemfRecord.class),
|
||||
setwindoworgex(0x0000000A, UnimplementedHemfRecord.class),
|
||||
setviewportextex(0x0000000B, UnimplementedHemfRecord.class),
|
||||
setviewportorgex(0x0000000C, UnimplementedHemfRecord.class),
|
||||
setbrushorgex(0x0000000D, UnimplementedHemfRecord.class),
|
||||
eof(0x0000000E, UnimplementedHemfRecord.class),
|
||||
setpixelv(0x0000000F, UnimplementedHemfRecord.class),
|
||||
setmapperflags(0x00000010, UnimplementedHemfRecord.class),
|
||||
setmapmode(0x00000011, UnimplementedHemfRecord.class),
|
||||
setbkmode(0x00000012, UnimplementedHemfRecord.class),
|
||||
setpolyfillmode(0x00000013, UnimplementedHemfRecord.class),
|
||||
setrop2(0x00000014, UnimplementedHemfRecord.class),
|
||||
setstretchbltmode(0x00000015, UnimplementedHemfRecord.class),
|
||||
settextalign(0x00000016, HemfText.SetTextAlign.class),
|
||||
setcoloradjustment(0x00000017, UnimplementedHemfRecord.class),
|
||||
settextcolor(0x00000018, HemfText.SetTextColor.class),
|
||||
setbkcolor(0x00000019, UnimplementedHemfRecord.class),
|
||||
setoffsetcliprgn(0x0000001A, UnimplementedHemfRecord.class),
|
||||
setmovetoex(0x0000001B, UnimplementedHemfRecord.class),
|
||||
setmetargn(0x0000001C, UnimplementedHemfRecord.class),
|
||||
setexcludecliprect(0x0000001D, UnimplementedHemfRecord.class),
|
||||
setintersectcliprect(0x0000001E, UnimplementedHemfRecord.class),
|
||||
scaleviewportextex(0x0000001F, UnimplementedHemfRecord.class),
|
||||
scalewindowextex(0x00000020, UnimplementedHemfRecord.class),
|
||||
savedc(0x00000021, UnimplementedHemfRecord.class),
|
||||
restoredc(0x00000022, UnimplementedHemfRecord.class),
|
||||
setworldtransform(0x00000023, UnimplementedHemfRecord.class),
|
||||
modifyworldtransform(0x00000024, UnimplementedHemfRecord.class),
|
||||
selectobject(0x00000025, UnimplementedHemfRecord.class),
|
||||
createpen(0x00000026, UnimplementedHemfRecord.class),
|
||||
createbrushindirect(0x00000027, UnimplementedHemfRecord.class),
|
||||
deleteobject(0x00000028, UnimplementedHemfRecord.class),
|
||||
anglearc(0x00000029, UnimplementedHemfRecord.class),
|
||||
ellipse(0x0000002A, UnimplementedHemfRecord.class),
|
||||
rectangle(0x0000002B, UnimplementedHemfRecord.class),
|
||||
roundirect(0x0000002C, UnimplementedHemfRecord.class),
|
||||
arc(0x0000002D, UnimplementedHemfRecord.class),
|
||||
chord(0x0000002E, UnimplementedHemfRecord.class),
|
||||
pie(0x0000002F, UnimplementedHemfRecord.class),
|
||||
selectpalette(0x00000030, UnimplementedHemfRecord.class),
|
||||
createpalette(0x00000031, UnimplementedHemfRecord.class),
|
||||
setpaletteentries(0x00000032, UnimplementedHemfRecord.class),
|
||||
resizepalette(0x00000033, UnimplementedHemfRecord.class),
|
||||
realizepalette(0x0000034, UnimplementedHemfRecord.class),
|
||||
extfloodfill(0x00000035, UnimplementedHemfRecord.class),
|
||||
lineto(0x00000036, UnimplementedHemfRecord.class),
|
||||
arcto(0x00000037, UnimplementedHemfRecord.class),
|
||||
polydraw(0x00000038, UnimplementedHemfRecord.class),
|
||||
setarcdirection(0x00000039, UnimplementedHemfRecord.class),
|
||||
setmiterlimit(0x0000003A, UnimplementedHemfRecord.class),
|
||||
beginpath(0x0000003B, UnimplementedHemfRecord.class),
|
||||
endpath(0x0000003C, UnimplementedHemfRecord.class),
|
||||
closefigure(0x0000003D, UnimplementedHemfRecord.class),
|
||||
fillpath(0x0000003E, UnimplementedHemfRecord.class),
|
||||
strokeandfillpath(0x0000003F, UnimplementedHemfRecord.class),
|
||||
strokepath(0x00000040, UnimplementedHemfRecord.class),
|
||||
flattenpath(0x00000041, UnimplementedHemfRecord.class),
|
||||
widenpath(0x00000042, UnimplementedHemfRecord.class),
|
||||
selectclippath(0x00000043, UnimplementedHemfRecord.class),
|
||||
abortpath(0x00000044, UnimplementedHemfRecord.class), //no 45?!
|
||||
comment(0x00000046, HemfCommentRecord.class),
|
||||
fillrgn(0x00000047, UnimplementedHemfRecord.class),
|
||||
framergn(0x00000048, UnimplementedHemfRecord.class),
|
||||
invertrgn(0x00000049, UnimplementedHemfRecord.class),
|
||||
paintrgn(0x0000004A, UnimplementedHemfRecord.class),
|
||||
extselectciprrgn(0x0000004B, UnimplementedHemfRecord.class),
|
||||
bitblt(0x0000004C, UnimplementedHemfRecord.class),
|
||||
stretchblt(0x0000004D, UnimplementedHemfRecord.class),
|
||||
maskblt(0x0000004E, UnimplementedHemfRecord.class),
|
||||
plgblt(0x0000004F, UnimplementedHemfRecord.class),
|
||||
setbitstodevice(0x00000050, UnimplementedHemfRecord.class),
|
||||
stretchdibits(0x00000051, UnimplementedHemfRecord.class),
|
||||
extcreatefontindirectw(0x00000052, HemfText.ExtCreateFontIndirectW.class),
|
||||
exttextouta(0x00000053, HemfText.ExtTextOutA.class),
|
||||
exttextoutw(0x00000054, HemfText.ExtTextOutW.class),
|
||||
polybezier16(0x00000055, UnimplementedHemfRecord.class),
|
||||
polygon16(0x00000056, UnimplementedHemfRecord.class),
|
||||
polyline16(0x00000057, UnimplementedHemfRecord.class),
|
||||
polybezierto16(0x00000058, UnimplementedHemfRecord.class),
|
||||
polylineto16(0x00000059, UnimplementedHemfRecord.class),
|
||||
polypolyline16(0x0000005A, UnimplementedHemfRecord.class),
|
||||
polypolygon16(0x0000005B, UnimplementedHemfRecord.class),
|
||||
polydraw16(0x0000005C, UnimplementedHemfRecord.class),
|
||||
createmonobrush16(0x0000005D, UnimplementedHemfRecord.class),
|
||||
createdibpatternbrushpt(0x0000005E, UnimplementedHemfRecord.class),
|
||||
extcreatepen(0x0000005F, UnimplementedHemfRecord.class),
|
||||
polytextouta(0x00000060, HemfText.PolyTextOutA.class),
|
||||
polytextoutw(0x00000061, HemfText.PolyTextOutW.class),
|
||||
seticmmode(0x00000062, UnimplementedHemfRecord.class),
|
||||
createcolorspace(0x00000063, UnimplementedHemfRecord.class),
|
||||
setcolorspace(0x00000064, UnimplementedHemfRecord.class),
|
||||
deletecolorspace(0x00000065, UnimplementedHemfRecord.class),
|
||||
glsrecord(0x00000066, UnimplementedHemfRecord.class),
|
||||
glsboundedrecord(0x00000067, UnimplementedHemfRecord.class),
|
||||
pixelformat(0x00000068, UnimplementedHemfRecord.class),
|
||||
drawescape(0x00000069, UnimplementedHemfRecord.class),
|
||||
extescape(0x0000006A, UnimplementedHemfRecord.class),//no 6b?!
|
||||
smalltextout(0x0000006C, UnimplementedHemfRecord.class),
|
||||
forceufimapping(0x0000006D, UnimplementedHemfRecord.class),
|
||||
namedescape(0x0000006E, UnimplementedHemfRecord.class),
|
||||
colorcorrectpalette(0x0000006F, UnimplementedHemfRecord.class),
|
||||
seticmprofilea(0x00000070, UnimplementedHemfRecord.class),
|
||||
seticmprofilew(0x00000071, UnimplementedHemfRecord.class),
|
||||
alphablend(0x00000072, UnimplementedHemfRecord.class),
|
||||
setlayout(0x00000073, UnimplementedHemfRecord.class),
|
||||
transparentblt(0x00000074, UnimplementedHemfRecord.class),
|
||||
gradientfill(0x00000076, UnimplementedHemfRecord.class), //no 75?!
|
||||
setlinkdufis(0x00000077, UnimplementedHemfRecord.class),
|
||||
settextjustification(0x00000078, HemfText.SetTextJustification.class),
|
||||
colormatchtargetw(0x00000079, UnimplementedHemfRecord.class),
|
||||
createcolorspacew(0x0000007A, UnimplementedHemfRecord.class);
|
||||
|
||||
public final long id;
|
||||
public final Class<? extends HemfRecord> clazz;
|
||||
|
||||
HemfRecordType(long id, Class<? extends HemfRecord> clazz) {
|
||||
this.id = id;
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public static HemfRecordType getById(long id) {
|
||||
for (HemfRecordType wrt : values()) {
|
||||
if (wrt.id == id) return wrt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
import org.apache.poi.util.RecordFormatException;
|
||||
|
||||
/**
|
||||
* Container class to gather all text-related commands
|
||||
* This is starting out as read only, and very little is actually
|
||||
* implemented at this point!
|
||||
*/
|
||||
@Internal
|
||||
public class HemfText {
|
||||
|
||||
private final static Charset UTF16LE = Charset.forName("UTF-16LE");
|
||||
|
||||
public static class ExtCreateFontIndirectW extends UnimplementedHemfRecord {
|
||||
}
|
||||
|
||||
public static class ExtTextOutA implements HemfRecord {
|
||||
|
||||
private long left,top,right,bottom;
|
||||
|
||||
//TODO: translate this to a graphicsmode enum
|
||||
private long graphicsMode;
|
||||
|
||||
private long exScale;
|
||||
private long eyScale;
|
||||
EmrTextObject textObject;
|
||||
|
||||
@Override
|
||||
public HemfRecordType getRecordType() {
|
||||
return HemfRecordType.exttextouta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long recordId, long recordSize) throws IOException {
|
||||
//note that the first 2 uInts have been read off and the recordsize has
|
||||
//been decreased by 8
|
||||
left = leis.readInt();
|
||||
top = leis.readInt();
|
||||
right = leis.readInt();
|
||||
bottom = leis.readInt();
|
||||
graphicsMode = leis.readUInt();
|
||||
exScale = leis.readUInt();
|
||||
eyScale = leis.readUInt();
|
||||
|
||||
int recordSizeInt = -1;
|
||||
if (recordSize < Integer.MAX_VALUE) {
|
||||
recordSizeInt = (int)recordSize;
|
||||
} else {
|
||||
throw new RecordFormatException("can't have text length > Integer.MAX_VALUE");
|
||||
}
|
||||
//guarantee to read the rest of the EMRTextObjectRecord
|
||||
//emrtextbytes start after 7*4 bytes read above
|
||||
byte[] emrTextBytes = new byte[recordSizeInt-(7*LittleEndian.INT_SIZE)];
|
||||
IOUtils.readFully(leis, emrTextBytes);
|
||||
textObject = new EmrTextObject(emrTextBytes, getEncodingHint(), 20);//should be 28, but recordSizeInt has already subtracted 8
|
||||
return recordSize;
|
||||
}
|
||||
|
||||
protected Charset getEncodingHint() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* To be implemented! We need to get the current character set
|
||||
* from the current font for {@link ExtTextOutA},
|
||||
* which has to be tracked in the playback device.
|
||||
*
|
||||
* For {@link ExtTextOutW}, the charset is "UTF-16LE"
|
||||
*
|
||||
* @param charset the charset to be used to decode the character bytes
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public String getText(Charset charset) throws IOException {
|
||||
return textObject.getText(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the x offset for the EmrTextObject
|
||||
*/
|
||||
public long getX() {
|
||||
return textObject.x;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the y offset for the EmrTextObject
|
||||
*/
|
||||
public long getY() {
|
||||
return textObject.y;
|
||||
}
|
||||
|
||||
public long getLeft() {
|
||||
return left;
|
||||
}
|
||||
|
||||
public long getTop() {
|
||||
return top;
|
||||
}
|
||||
|
||||
public long getRight() {
|
||||
return right;
|
||||
}
|
||||
|
||||
public long getBottom() {
|
||||
return bottom;
|
||||
}
|
||||
|
||||
public long getGraphicsMode() {
|
||||
return graphicsMode;
|
||||
}
|
||||
|
||||
public long getExScale() {
|
||||
return exScale;
|
||||
}
|
||||
|
||||
public long getEyScale() {
|
||||
return eyScale;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ExtTextOutW extends ExtTextOutA {
|
||||
|
||||
@Override
|
||||
public HemfRecordType getRecordType() {
|
||||
return HemfRecordType.exttextoutw;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Charset getEncodingHint() {
|
||||
return UTF16LE;
|
||||
}
|
||||
|
||||
public String getText() throws IOException {
|
||||
return getText(UTF16LE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Needs to be implemented. Couldn't find example.
|
||||
*/
|
||||
public static class PolyTextOutA extends UnimplementedHemfRecord {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Needs to be implemented. Couldn't find example.
|
||||
*/
|
||||
public static class PolyTextOutW extends UnimplementedHemfRecord {
|
||||
|
||||
}
|
||||
|
||||
public static class SetTextAlign extends UnimplementedHemfRecord {
|
||||
}
|
||||
|
||||
public static class SetTextColor extends UnimplementedHemfRecord {
|
||||
}
|
||||
|
||||
|
||||
public static class SetTextJustification extends UnimplementedHemfRecord {
|
||||
|
||||
}
|
||||
|
||||
private static class EmrTextObject {
|
||||
long x;
|
||||
long y;
|
||||
int numChars;
|
||||
byte[] rawTextBytes;//this stores _all_ of the bytes to the end of the EMRTextObject record.
|
||||
//Because of potential variable length encodings, must
|
||||
//carefully read only the numChars from this byte array.
|
||||
|
||||
EmrTextObject(byte[] emrTextObjBytes, Charset charsetHint, int readSoFar) throws IOException {
|
||||
|
||||
int offset = 0;
|
||||
x = LittleEndian.getUInt(emrTextObjBytes, offset); offset+= LittleEndian.INT_SIZE;
|
||||
y = LittleEndian.getUInt(emrTextObjBytes, offset); offset+= LittleEndian.INT_SIZE;
|
||||
long numCharsLong = LittleEndian.getUInt(emrTextObjBytes, offset); offset += LittleEndian.INT_SIZE;
|
||||
long offString = LittleEndian.getUInt(emrTextObjBytes, offset); offset += LittleEndian.INT_SIZE;
|
||||
int start = (int)offString-offset-readSoFar;
|
||||
|
||||
if (numCharsLong == 0) {
|
||||
rawTextBytes = new byte[0];
|
||||
numChars = 0;
|
||||
return;
|
||||
}
|
||||
if (numCharsLong > Integer.MAX_VALUE) {
|
||||
throw new RecordFormatException("Number of characters can't be > Integer.MAX_VALUE");
|
||||
}
|
||||
numChars = (int)numCharsLong;
|
||||
rawTextBytes = new byte[emrTextObjBytes.length-start];
|
||||
System.arraycopy(emrTextObjBytes, start, rawTextBytes, 0, emrTextObjBytes.length-start);
|
||||
}
|
||||
|
||||
String getText(Charset charset) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Reader r = null;
|
||||
try {
|
||||
r = new InputStreamReader(new ByteArrayInputStream(rawTextBytes), charset);
|
||||
for (int i = 0; i < numChars; i++) {
|
||||
sb.appendCodePoint(readCodePoint(r));
|
||||
}
|
||||
} finally {
|
||||
IOUtils.closeQuietly(r);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
//TODO: move this to IOUtils?
|
||||
private int readCodePoint(Reader r) throws IOException {
|
||||
int c1 = r.read();
|
||||
if (c1 == -1) {
|
||||
throw new EOFException("Tried to read beyond byte array");
|
||||
}
|
||||
if (!Character.isHighSurrogate((char)c1)) {
|
||||
return c1;
|
||||
}
|
||||
int c2 = r.read();
|
||||
if (c2 == -1) {
|
||||
throw new EOFException("Tried to read beyond byte array");
|
||||
}
|
||||
if (!Character.isLowSurrogate((char)c2)) {
|
||||
throw new RecordFormatException("Expected low surrogate after high surrogate");
|
||||
}
|
||||
return Character.toCodePoint((char)c1, (char)c2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* ====================================================================
|
||||
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.hemf.record;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
|
||||
@Internal
|
||||
public class UnimplementedHemfRecord implements HemfRecord {
|
||||
|
||||
private long recordId;
|
||||
public UnimplementedHemfRecord() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public HemfRecordType getRecordType() {
|
||||
return HemfRecordType.getById(recordId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long recordId, long recordSize) throws IOException {
|
||||
this.recordId = recordId;
|
||||
long skipped = IOUtils.skipFully(leis, recordSize);
|
||||
if (skipped < 0) {
|
||||
throw new IOException("End of stream reached before record read");
|
||||
}
|
||||
return skipped;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/* ====================================================================
|
||||
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.hemf.extractor;
|
||||
|
||||
import static org.apache.poi.POITestCase.assertContains;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.poi.POIDataSamples;
|
||||
import org.apache.poi.hemf.record.AbstractHemfComment;
|
||||
import org.apache.poi.hemf.record.HemfCommentPublic;
|
||||
import org.apache.poi.hemf.record.HemfCommentRecord;
|
||||
import org.apache.poi.hemf.record.HemfHeader;
|
||||
import org.apache.poi.hemf.record.HemfRecord;
|
||||
import org.apache.poi.hemf.record.HemfRecordType;
|
||||
import org.apache.poi.hemf.record.HemfText;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HemfExtractorTest {
|
||||
|
||||
@Test
|
||||
public void testBasicWindows() throws Exception {
|
||||
InputStream is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SimpleEMF_windows.emf");
|
||||
HemfExtractor ex = new HemfExtractor(is);
|
||||
HemfHeader header = ex.getHeader();
|
||||
assertEquals(27864, header.getBytes());
|
||||
assertEquals(31, header.getRecords());
|
||||
assertEquals(3, header.getHandles());
|
||||
assertEquals(346000, header.getMicrometersX());
|
||||
assertEquals(194000, header.getMicrometersY());
|
||||
|
||||
int records = 0;
|
||||
for (HemfRecord record : ex) {
|
||||
records++;
|
||||
}
|
||||
|
||||
assertEquals(header.getRecords() - 1, records);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicMac() throws Exception {
|
||||
InputStream is =
|
||||
POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SimpleEMF_mac.emf");
|
||||
HemfExtractor ex = new HemfExtractor(is);
|
||||
HemfHeader header = ex.getHeader();
|
||||
|
||||
int records = 0;
|
||||
boolean extractedData = false;
|
||||
for (HemfRecord record : ex) {
|
||||
if (record.getRecordType() == HemfRecordType.comment) {
|
||||
AbstractHemfComment comment = ((HemfCommentRecord) record).getComment();
|
||||
if (comment instanceof HemfCommentPublic.MultiFormats) {
|
||||
for (HemfCommentPublic.HemfMultiFormatsData d : ((HemfCommentPublic.MultiFormats) comment).getData()) {
|
||||
byte[] data = d.getData();
|
||||
//make sure header starts at 0
|
||||
assertEquals('%', data[0]);
|
||||
assertEquals('P', data[1]);
|
||||
assertEquals('D', data[2]);
|
||||
assertEquals('F', data[3]);
|
||||
|
||||
//make sure byte array ends at EOF\n
|
||||
assertEquals('E', data[data.length - 4]);
|
||||
assertEquals('O', data[data.length - 3]);
|
||||
assertEquals('F', data[data.length - 2]);
|
||||
assertEquals('\n', data[data.length - 1]);
|
||||
extractedData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
records++;
|
||||
}
|
||||
assertTrue(extractedData);
|
||||
assertEquals(header.getRecords() - 1, records);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacText() throws Exception {
|
||||
InputStream is =
|
||||
POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SimpleEMF_mac.emf");
|
||||
HemfExtractor ex = new HemfExtractor(is);
|
||||
|
||||
long lastY = -1;
|
||||
long lastX = -1;
|
||||
long fudgeFactorX = 1000;//derive this from the font information!
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (HemfRecord record : ex) {
|
||||
if (record.getRecordType().equals(HemfRecordType.exttextoutw)) {
|
||||
HemfText.ExtTextOutW extTextOutW = (HemfText.ExtTextOutW) record;
|
||||
if (lastY > -1 && lastY != extTextOutW.getY()) {
|
||||
sb.append("\n");
|
||||
lastX = -1;
|
||||
}
|
||||
if (lastX > -1 && extTextOutW.getX() - lastX > fudgeFactorX) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(extTextOutW.getText());
|
||||
lastY = extTextOutW.getY();
|
||||
lastX = extTextOutW.getX();
|
||||
}
|
||||
}
|
||||
String txt = sb.toString();
|
||||
assertContains(txt, "Tika http://incubator.apache.org");
|
||||
assertContains(txt, "Latest News\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsText() throws Exception {
|
||||
InputStream is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SimpleEMF_windows.emf");
|
||||
HemfExtractor ex = new HemfExtractor(is);
|
||||
long lastY = -1;
|
||||
long lastX = -1;
|
||||
long fudgeFactorX = 1000;//derive this from the font or frame/bounds information
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Set<String> expectedParts = new HashSet<String>();
|
||||
expectedParts.add("C:\\Users\\tallison\\");
|
||||
expectedParts.add("testPDF.pdf");
|
||||
int foundExpected = 0;
|
||||
for (HemfRecord record : ex) {
|
||||
if (record.getRecordType().equals(HemfRecordType.exttextoutw)) {
|
||||
HemfText.ExtTextOutW extTextOutW = (HemfText.ExtTextOutW) record;
|
||||
if (lastY > -1 && lastY != extTextOutW.getY()) {
|
||||
sb.append("\n");
|
||||
lastX = -1;
|
||||
}
|
||||
if (lastX > -1 && extTextOutW.getX() - lastX > fudgeFactorX) {
|
||||
sb.append(" ");
|
||||
}
|
||||
String txt = extTextOutW.getText();
|
||||
if (expectedParts.contains(txt)) {
|
||||
foundExpected++;
|
||||
}
|
||||
sb.append(txt);
|
||||
lastY = extTextOutW.getY();
|
||||
lastX = extTextOutW.getX();
|
||||
}
|
||||
}
|
||||
String txt = sb.toString();
|
||||
assertContains(txt, "C:\\Users\\tallison\\\n");
|
||||
assertContains(txt, "asf2-git-1.x\\tika-\n");
|
||||
assertEquals(expectedParts.size(), foundExpected);
|
||||
}
|
||||
|
||||
/*
|
||||
govdocs1 064213.doc-0.emf contains an example of extextouta
|
||||
*/
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/* ====================================================================
|
||||
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.hemf.hemfplus.extractor;
|
||||
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.POIDataSamples;
|
||||
import org.apache.poi.hemf.extractor.HemfExtractor;
|
||||
import org.apache.poi.hemf.hemfplus.record.HemfPlusHeader;
|
||||
import org.apache.poi.hemf.hemfplus.record.HemfPlusRecord;
|
||||
import org.apache.poi.hemf.hemfplus.record.HemfPlusRecordType;
|
||||
import org.apache.poi.hemf.record.HemfCommentEMFPlus;
|
||||
import org.apache.poi.hemf.record.HemfCommentRecord;
|
||||
import org.apache.poi.hemf.record.HemfRecord;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HemfPlusExtractorTest {
|
||||
|
||||
@Test
|
||||
public void testBasic() throws Exception {
|
||||
//test header
|
||||
HemfCommentEMFPlus emfPlus = getCommentRecord("SimpleEMF_windows.emf", 0);
|
||||
List<HemfPlusRecord> records = emfPlus.getRecords();
|
||||
assertEquals(1, records.size());
|
||||
assertEquals(HemfPlusRecordType.header, records.get(0).getRecordType());
|
||||
|
||||
HemfPlusHeader header = (HemfPlusHeader)records.get(0);
|
||||
assertEquals(240, header.getLogicalDpiX());
|
||||
assertEquals(240, header.getLogicalDpiY());
|
||||
assertEquals(1, header.getFlags());
|
||||
assertEquals(1, header.getEmfPlusFlags());
|
||||
|
||||
|
||||
|
||||
//test that the HemfCommentEMFPlus record at offset 1
|
||||
//contains 6 HemfCommentEMFPlus records within it
|
||||
List<HemfPlusRecordType> expected = new ArrayList<HemfPlusRecordType>();
|
||||
expected.add(HemfPlusRecordType.setPixelOffsetMode);
|
||||
expected.add(HemfPlusRecordType.setAntiAliasMode);
|
||||
expected.add(HemfPlusRecordType.setCompositingQuality);
|
||||
expected.add(HemfPlusRecordType.setPageTransform);
|
||||
expected.add(HemfPlusRecordType.setInterpolationMode);
|
||||
expected.add(HemfPlusRecordType.getDC);
|
||||
|
||||
emfPlus = getCommentRecord("SimpleEMF_windows.emf", 1);
|
||||
records = emfPlus.getRecords();
|
||||
assertEquals(expected.size(), records.size());
|
||||
|
||||
for (int i = 0; i < expected.size(); i++) {
|
||||
assertEquals(expected.get(i), records.get(i).getRecordType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private HemfCommentEMFPlus getCommentRecord(String testFileName, int recordIndex) throws Exception {
|
||||
InputStream is = null;
|
||||
HemfCommentEMFPlus returnRecord = null;
|
||||
|
||||
try {
|
||||
is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream(testFileName);
|
||||
HemfExtractor ex = new HemfExtractor(is);
|
||||
int i = 0;
|
||||
for (HemfRecord record : ex) {
|
||||
if (i == recordIndex) {
|
||||
HemfCommentRecord commentRecord = ((HemfCommentRecord) record);
|
||||
returnRecord = (HemfCommentEMFPlus) commentRecord.getComment();
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
return returnRecord;
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue