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
2abd64f3a9
commit
f6388c2fff
@ -251,4 +251,27 @@ public final class IOUtils {
|
|||||||
exc );
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
198
src/scratchpad/src/org/apache/poi/hemf/record/HemfHeader.java
Normal file
198
src/scratchpad/src/org/apache/poi/hemf/record/HemfHeader.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
262
src/scratchpad/src/org/apache/poi/hemf/record/HemfText.java
Normal file
262
src/scratchpad/src/org/apache/poi/hemf/record/HemfText.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
BIN
test-data/document/testException2.doc-2.wmf
Normal file
BIN
test-data/document/testException2.doc-2.wmf
Normal file
Binary file not shown.
BIN
test-data/spreadsheet/SimpleEMF_mac.emf
Normal file
BIN
test-data/spreadsheet/SimpleEMF_mac.emf
Normal file
Binary file not shown.
BIN
test-data/spreadsheet/SimpleEMF_windows.emf
Normal file
BIN
test-data/spreadsheet/SimpleEMF_windows.emf
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user