WMF fixes
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1722465 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
069d7c6141
commit
ee6966dae9
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
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()]);
|
||||
int size = 9*LittleEndianConsts.SHORT_SIZE+LittleEndianConsts.INT_SIZE;
|
||||
|
||||
return 20 + size;
|
||||
scanObjects = new WmfScanObject[scanCount];
|
||||
for (int i=0; i<scanCount; i++) {
|
||||
size += (scanObjects[i] = new WmfScanObject()).init(leis);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user