WMF fixes

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1722465 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2015-12-31 20:23:20 +00:00
parent 069d7c6141
commit ee6966dae9
7 changed files with 128 additions and 129 deletions

View File

@ -76,33 +76,8 @@ public class HwmfBitmap16 {
byte buf[] = new byte[bytes];
leis.read(buf);
// FileOutputStream fos = new FileOutputStream("bla16.bmp");
// fos.write(buf);
// fos.close();
// BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//
// int size2 = 0;
// byte buf[] = new byte[widthBytes];
// for (int h=0; h<height; h++) {
// leis.read(buf);
// size2 += widthBytes;
//
// ImageInputStream iis = new MemoryCacheImageInputStream(new ByteArrayInputStream(buf));
//
// for (int w=0; w<width; w++) {
// long bitsAtPixel = iis.readBits(bitsPixel);
// // TODO: is bitsPixel a multiple of 3 (r,g,b)
// // which colortable should be used for the various bit sizes???
//
// }
// }
//
// assert (bytes == size2);
//
// size += size2;
// TODO: this is not implemented ... please provide a sample, if it
// ever happens to you, to come here ...
return size;
}

View File

@ -22,6 +22,7 @@ import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@ -207,7 +208,7 @@ public class HwmfBitmapDib {
@SuppressWarnings("unused")
private Color colorTable[];
@SuppressWarnings("unused")
private int colorMaskRed=0,colorMaskGreen=0,colorMaskBlue=0;
private int colorMaskR=0,colorMaskG=0,colorMaskB=0;
// size of header and color table, for start of image data calculation
private int introSize;
@ -222,7 +223,7 @@ public class HwmfBitmapDib {
introSize += readColors(leis);
assert(introSize < 10000);
int fileSize = (headerImageSize != 0) ? (int)(introSize+headerImageSize) : recordSize;
int fileSize = (headerImageSize < headerSize) ? recordSize : (int)Math.min(introSize+headerImageSize,recordSize);
imageData = new byte[fileSize];
leis.reset();
@ -316,41 +317,43 @@ public class HwmfBitmapDib {
return 0;
case BI_BITCOUNT_1:
// 2 colors
return readRGBQuad(leis, 2);
return readRGBQuad(leis, (int)(headerColorUsed == 0 ? 2 : Math.min(headerColorUsed,2)));
case BI_BITCOUNT_2:
// 16 colors
return readRGBQuad(leis, 16);
return readRGBQuad(leis, (int)(headerColorUsed == 0 ? 16 : Math.min(headerColorUsed,16)));
case BI_BITCOUNT_3:
// 256 colors
return readRGBQuad(leis, (int)headerColorUsed);
case BI_BITCOUNT_5:
colorMaskRed=0xFF;
colorMaskGreen=0xFF;
colorMaskBlue=0xFF;
return 0;
return readRGBQuad(leis, (int)(headerColorUsed == 0 ? 256 : Math.min(headerColorUsed,256)));
case BI_BITCOUNT_4:
if (headerCompression == Compression.BI_RGB) {
colorMaskBlue = 0x1F;
colorMaskGreen = 0x1F<<5;
colorMaskRed = 0x1F<<10;
switch (headerCompression) {
case BI_RGB:
colorMaskB = 0x1F;
colorMaskG = 0x1F<<5;
colorMaskR = 0x1F<<10;
return 0;
} else {
assert(headerCompression == Compression.BI_BITFIELDS);
colorMaskBlue = leis.readInt();
colorMaskGreen = leis.readInt();
colorMaskRed = leis.readInt();
case BI_BITFIELDS:
colorMaskB = leis.readInt();
colorMaskG = leis.readInt();
colorMaskR = leis.readInt();
return 3*LittleEndianConsts.INT_SIZE;
default:
throw new IOException("Invalid compression option ("+headerCompression+") for bitcount ("+headerBitCount+").");
}
case BI_BITCOUNT_5:
case BI_BITCOUNT_6:
if (headerCompression == Compression.BI_RGB) {
colorMaskBlue = colorMaskGreen = colorMaskRed = 0xFF;
switch (headerCompression) {
case BI_RGB:
colorMaskR=0xFF;
colorMaskG=0xFF;
colorMaskB=0xFF;
return 0;
} else {
assert(headerCompression == Compression.BI_BITFIELDS);
colorMaskBlue = leis.readInt();
colorMaskGreen = leis.readInt();
colorMaskRed = leis.readInt();
case BI_BITFIELDS:
colorMaskB = leis.readInt();
colorMaskG = leis.readInt();
colorMaskR = leis.readInt();
return 3*LittleEndianConsts.INT_SIZE;
default:
throw new IOException("Invalid compression option ("+headerCompression+") for bitcount ("+headerBitCount+").");
}
}
}
@ -372,30 +375,36 @@ public class HwmfBitmapDib {
return size;
}
public BufferedImage getImage() {
public InputStream getBMPStream() {
if (imageData == null) {
throw new RecordFormatException("bitmap not initialized ... need to call init() before");
}
// sometimes there are missing bytes after the imageData which will be 0-filled
int imageSize = (int)Math.max(imageData.length, introSize+headerImageSize);
// create the image data and leave the parsing to the ImageIO api
byte buf[] = new byte[BMP_HEADER_SIZE+imageData.length];
byte buf[] = new byte[BMP_HEADER_SIZE+imageSize];
// https://en.wikipedia.org/wiki/BMP_file_format # Bitmap file header
buf[0] = (byte)'B';
buf[1] = (byte)'M';
// the full size of the bmp
LittleEndian.putInt(buf, 2, (int)(BMP_HEADER_SIZE + introSize + headerImageSize));
LittleEndian.putInt(buf, 2, BMP_HEADER_SIZE+imageSize);
// the next 4 bytes are unused
LittleEndian.putInt(buf, 6, 0);
// start of image = BMP header length + dib header length + color tables length
LittleEndian.putInt(buf, 10, BMP_HEADER_SIZE + introSize);
// fill the "known" image data
System.arraycopy(imageData, 0, buf, BMP_HEADER_SIZE, imageData.length);
return new ByteArrayInputStream(buf);
}
public BufferedImage getImage() {
try {
return ImageIO.read(new ByteArrayInputStream(buf));
return ImageIO.read(getBMPStream());
} catch (IOException e) {
// ... shouldn't happen
throw new RecordFormatException("invalid bitmap data", e);
}
}

View File

@ -614,7 +614,7 @@ public class HwmfFill {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3));
boolean hasBitmap = (recordSize/2 != ((recordFunction >> 8) + 3));
int size = 0;
int rasterOpCode = leis.readUShort();
@ -802,7 +802,7 @@ public class HwmfFill {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3));
boolean hasBitmap = (recordSize/2 != ((recordFunction >> 8) + 3));
int size = 0;
int rasterOpCode = leis.readUShort();
@ -840,7 +840,7 @@ public class HwmfFill {
@Override
public BufferedImage getImage() {
return target.getImage();
return (target == null) ? null : target.getImage();
}
}

View File

@ -259,6 +259,9 @@ public class HwmfText {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
// -6 bytes of record function and length header
final int remainingRecordSize = (int)(recordSize-6);
y = leis.readShort();
x = leis.readShort();
stringLength = leis.readShort();
@ -266,7 +269,7 @@ public class HwmfText {
int size = 4*LittleEndianConsts.SHORT_SIZE;
if (fwOpts != 0) {
if (fwOpts != 0 && size+8<=remainingRecordSize) {
// the bounding rectangle is optional and only read when fwOpts are given
left = leis.readShort();
top = leis.readShort();
@ -280,8 +283,6 @@ public class HwmfText {
text = new String(buf, 0, stringLength, LocaleUtil.CHARSET_1252);
size += buf.length;
// -6 bytes of record function and length header
int remainingRecordSize = (int)(recordSize-6);
if (size < remainingRecordSize) {
if (size + stringLength*LittleEndianConsts.SHORT_SIZE < remainingRecordSize) {
throw new RecordFormatException("can't read Dx array - given recordSize doesn't contain enough values for string length "+stringLength);

View File

@ -542,14 +542,17 @@ public class HwmfWindowing {
count = leis.readUShort();
top = leis.readUShort();
bottom = leis.readUShort();
left_scanline = new int[count];
right_scanline = new int[count];
for (int i=0; i*2<count; i++) {
int size = 3*LittleEndianConsts.SHORT_SIZE;
left_scanline = new int[count/2];
right_scanline = new int[count/2];
for (int i=0; i<count/2; i++) {
left_scanline[i] = leis.readUShort();
right_scanline[i] = leis.readUShort();
size += 2*LittleEndianConsts.SHORT_SIZE;
}
count2 = leis.readUShort();
return 8 + count*4;
size += LittleEndianConsts.SHORT_SIZE;
return size;
}
}
@ -618,26 +621,23 @@ public class HwmfWindowing {
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
nextInChain = leis.readShort();
objectType = leis.readShort();
objectCount = leis.readUShort();
objectCount = leis.readInt();
regionSize = leis.readShort();
scanCount = leis.readShort();
maxScan = leis.readShort();
bottom = leis.readShort();
right = leis.readShort();
top = leis.readShort();
left = leis.readShort();
top = leis.readShort();
right = leis.readShort();
bottom = leis.readShort();
int size = 9*LittleEndianConsts.SHORT_SIZE+LittleEndianConsts.INT_SIZE;
List<WmfScanObject> soList = new ArrayList<WmfScanObject>();
int scanCountI = 0, size = 0;
do {
WmfScanObject so = new WmfScanObject();
size += so.init(leis);
scanCountI += so.count;
soList.add(so);
} while (scanCountI < scanCount);
scanObjects = soList.toArray(new WmfScanObject[soList.size()]);
scanObjects = new WmfScanObject[scanCount];
for (int i=0; i<scanCount; i++) {
size += (scanObjects[i] = new WmfScanObject()).init(leis);
}
return 20 + size;
return size;
}
@Override

View File

@ -70,12 +70,8 @@ public class HwmfPicture {
int remainingSize = (int)(recordSize - consumedSize);
assert(remainingSize >= 0);
if (remainingSize > 0) {
// byte remaining[] = new byte[remainingSize];
// leis.read(remaining);
// FileOutputStream fos = new FileOutputStream("remaining.dat");
// fos.write(remaining);
// fos.close();
leis.skip(remainingSize);
// skip size in loops, because not always all bytes are skipped in one call
for (int i=remainingSize; i>0; i-=leis.skip(i));
}
}
}

View File

@ -20,14 +20,17 @@ package org.apache.poi.hwmf;
import static org.junit.Assert.assertEquals;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.imageio.ImageIO;
@ -55,55 +58,70 @@ public class TestHwmfParsing {
@Test
@Ignore
public void extract() throws IOException {
File dir = new File("test-data/slideshow");
File files[] = dir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().matches("(?i).*\\.pptx?$");
}
});
boolean outputFiles = false;
public void fetchWmfFromGovdocs() throws IOException {
URL url = new URL("http://digitalcorpora.org/corpora/files/govdocs1/by_type/ppt.zip");
File outdir = new File("build/ppt");
if (outputFiles) {
outdir.mkdirs();
}
int wmfIdx = 1;
for (File f : files) {
outdir.mkdirs();
ZipInputStream zis = new ZipInputStream(url.openStream());
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
String basename = ze.getName().replaceAll(".*?([^/]+)\\.wmf", "$1");
FilterInputStream fis = new FilterInputStream(zis){
public void close() throws IOException {}
};
try {
SlideShow<?,?> ss = SlideShowFactory.create(f);
SlideShow<?,?> ss = SlideShowFactory.create(fis);
int wmfIdx = 1;
for (PictureData pd : ss.getPictureData()) {
if (pd.getType() != PictureType.WMF) continue;
byte wmfData[] = pd.getData();
if (outputFiles) {
String filename = String.format(Locale.ROOT, "pic%04d.wmf", wmfIdx);
FileOutputStream fos = new FileOutputStream(new File(outdir, filename));
fos.write(wmfData);
fos.close();
}
HwmfPicture wmf = new HwmfPicture(new ByteArrayInputStream(wmfData));
int bmpIndex = 1;
for (HwmfRecord r : wmf.getRecords()) {
if (r instanceof HwmfImageRecord) {
BufferedImage bi = ((HwmfImageRecord)r).getImage();
if (outputFiles) {
String filename = String.format(Locale.ROOT, "pic%04d-%04d.png", wmfIdx, bmpIndex);
ImageIO.write(bi, "PNG", new File(outdir, filename));
}
bmpIndex++;
}
}
String filename = String.format(Locale.ROOT, "%s-%04d.wmf", basename, wmfIdx);
FileOutputStream fos = new FileOutputStream(new File(outdir, filename));
fos.write(wmfData);
fos.close();
wmfIdx++;
}
ss.close();
} catch (Exception e) {
System.out.println(f+" ignored.");
System.out.println(ze.getName()+" ignored.");
}
}
}
@Test
@Ignore
public void parseWmfs() throws IOException {
boolean outputFiles = false;
File indir = new File("build/ppt"), outdir = indir;
final String startFile = "";
File files[] = indir.listFiles(new FileFilter() {
boolean foundStartFile = false;
public boolean accept(File pathname) {
foundStartFile |= startFile.isEmpty() || pathname.getName().contains(startFile);
return foundStartFile && pathname.getName().matches("(?i).*\\.wmf?$");
}
});
for (File f : files) {
try {
String basename = f.getName().replaceAll(".*?([^/]+)\\.wmf", "$1");
FileInputStream fis = new FileInputStream(f);
HwmfPicture wmf = new HwmfPicture(fis);
fis.close();
int bmpIndex = 1;
for (HwmfRecord r : wmf.getRecords()) {
if (r instanceof HwmfImageRecord) {
BufferedImage bi = ((HwmfImageRecord)r).getImage();
if (bi != null && outputFiles) {
String filename = String.format(Locale.ROOT, "%s-%04d.png", basename, bmpIndex);
ImageIO.write(bi, "PNG", new File(outdir, filename));
}
bmpIndex++;
}
}
} catch (Exception e) {
System.out.println(f.getName()+" ignored.");
}
}
}
}