WMF fixes

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1722046 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2015-12-29 00:45:59 +00:00
parent ae1020ac35
commit a89d5210c3
20 changed files with 2001 additions and 708 deletions

View File

@ -0,0 +1,189 @@
/* ====================================================================
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.hwmf.draw;
import java.awt.Color;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import org.apache.poi.hwmf.record.HwmfBrushStyle;
import org.apache.poi.hwmf.record.HwmfColorRef;
import org.apache.poi.hwmf.record.HwmfFill.WmfSetPolyfillMode.HwmfPolyfillMode;
import org.apache.poi.hwmf.record.HwmfHatchStyle;
import org.apache.poi.hwmf.record.HwmfMapMode;
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
import org.apache.poi.hwmf.record.HwmfPenStyle;
public class HwmfDrawProperties {
private Rectangle2D window = new Rectangle2D.Double(0, 0, 1, 1);
private Rectangle2D viewport = new Rectangle2D.Double(0, 0, 1, 1);
private Point2D location = new Point2D.Double(0,0);
private HwmfMapMode mapMode = HwmfMapMode.MM_ANISOTROPIC;
private HwmfColorRef backgroundColor = new HwmfColorRef(Color.BLACK);
private HwmfBrushStyle brushStyle = HwmfBrushStyle.BS_SOLID;
private HwmfColorRef brushColor = new HwmfColorRef(Color.BLACK);
private HwmfHatchStyle brushHatch = HwmfHatchStyle.HS_HORIZONTAL;
private BufferedImage brushBitmap = null;
private double penWidth = 1;
private HwmfPenStyle penStyle = HwmfPenStyle.valueOf(0);
private HwmfColorRef penColor = new HwmfColorRef(Color.BLACK);
private double penMiterLimit = 10;
private HwmfBkMode bkMode = HwmfBkMode.OPAQUE;
private HwmfPolyfillMode polyfillMode = HwmfPolyfillMode.WINDING;
public void setViewportExt(double width, double height) {
double x = viewport.getX();
double y = viewport.getY();
double w = (width != 0) ? width : viewport.getWidth();
double h = (height != 0) ? height : viewport.getHeight();
viewport.setRect(x, y, w, h);
}
public void setViewportOrg(double x, double y) {
double w = viewport.getWidth();
double h = viewport.getHeight();
viewport.setRect(x, y, w, h);
}
public Rectangle2D getViewport() {
return (Rectangle2D)viewport.clone();
}
public void setWindowExt(double width, double height) {
double x = window.getX();
double y = window.getY();
double w = (width != 0) ? width : window.getWidth();
double h = (height != 0) ? height : window.getHeight();
window.setRect(x, y, w, h);
}
public void setWindowOrg(double x, double y) {
double w = window.getWidth();
double h = window.getHeight();
window.setRect(x, y, w, h);
}
public Rectangle2D getWindow() {
return (Rectangle2D)window.clone();
}
public void setLocation(double x, double y) {
location.setLocation(x, y);
}
public Point2D getLocation() {
return (Point2D)location.clone();
}
public void setMapMode(HwmfMapMode mapMode) {
this.mapMode = mapMode;
}
public HwmfMapMode getMapMode() {
return mapMode;
}
public HwmfBrushStyle getBrushStyle() {
return brushStyle;
}
public void setBrushStyle(HwmfBrushStyle brushStyle) {
this.brushStyle = brushStyle;
}
public HwmfHatchStyle getBrushHatch() {
return brushHatch;
}
public void setBrushHatch(HwmfHatchStyle brushHatch) {
this.brushHatch = brushHatch;
}
public HwmfColorRef getBrushColor() {
return brushColor;
}
public void setBrushColor(HwmfColorRef brushColor) {
this.brushColor = brushColor;
}
public HwmfBkMode getBkMode() {
return bkMode;
}
public void setBkMode(HwmfBkMode bkMode) {
this.bkMode = bkMode;
}
public HwmfPenStyle getPenStyle() {
return penStyle;
}
public void setPenStyle(HwmfPenStyle penStyle) {
this.penStyle = penStyle;
}
public HwmfColorRef getPenColor() {
return penColor;
}
public void setPenColor(HwmfColorRef penColor) {
this.penColor = penColor;
}
public double getPenWidth() {
return penWidth;
}
public void setPenWidth(double penWidth) {
this.penWidth = penWidth;
}
public double getPenMiterLimit() {
return penMiterLimit;
}
public void setPenMiterLimit(double penMiterLimit) {
this.penMiterLimit = penMiterLimit;
}
public HwmfColorRef getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(HwmfColorRef backgroundColor) {
this.backgroundColor = backgroundColor;
}
public HwmfPolyfillMode getPolyfillMode() {
return polyfillMode;
}
public void setPolyfillMode(HwmfPolyfillMode polyfillMode) {
this.polyfillMode = polyfillMode;
}
public BufferedImage getBrushBitmap() {
return brushBitmap;
}
public void setBrushBitmap(BufferedImage brushBitmap) {
this.brushBitmap = brushBitmap;
}
}

View File

@ -0,0 +1,185 @@
/* ====================================================================
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.hwmf.draw;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayDeque;
import java.util.Deque;
import org.apache.poi.hwmf.record.HwmfBrushStyle;
import org.apache.poi.hwmf.record.HwmfHatchStyle;
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
import org.apache.poi.hwmf.record.HwmfPenStyle;
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
import org.apache.poi.util.Units;
public class HwmfGraphics {
private final Graphics2D graphicsCtx;
private final Deque<HwmfDrawProperties> propStack = new ArrayDeque<HwmfDrawProperties>();
HwmfDrawProperties prop;
public HwmfGraphics(Graphics2D graphicsCtx) {
this.graphicsCtx = graphicsCtx;
prop = new HwmfDrawProperties();
propStack.push(prop);
}
public HwmfDrawProperties getProperties() {
return prop;
}
public void draw(Shape shape) {
HwmfLineDash lineDash = prop.getPenStyle().getLineDash();
if (lineDash == HwmfLineDash.NULL) {
// line is not drawn
return;
}
Shape tshape = fitShapeToView(shape);
BasicStroke stroke = getStroke();
// first draw a solid background line (depending on bkmode)
// only makes sense if the line is not solid
if (prop.getBkMode() == HwmfBkMode.OPAQUE && (lineDash != HwmfLineDash.SOLID && lineDash != HwmfLineDash.INSIDEFRAME)) {
graphicsCtx.setStroke(new BasicStroke(stroke.getLineWidth()));
graphicsCtx.setColor(prop.getBackgroundColor().getColor());
graphicsCtx.draw(tshape);
}
// then draw the (dashed) line
graphicsCtx.setStroke(stroke);
graphicsCtx.setColor(prop.getPenColor().getColor());
graphicsCtx.draw(tshape);
}
public void fill(Shape shape) {
if (prop.getBrushStyle() != HwmfBrushStyle.BS_NULL) {
GeneralPath gp = new GeneralPath(shape);
gp.setWindingRule(prop.getPolyfillMode().awtFlag);
Shape tshape = fitShapeToView(gp);
graphicsCtx.setPaint(getFill());
graphicsCtx.fill(tshape);
}
draw(shape);
}
protected Shape fitShapeToView(Shape shape) {
int scaleUnits = prop.getMapMode().scale;
Rectangle2D view = prop.getViewport(), win = prop.getWindow();
double scaleX, scaleY;
switch (scaleUnits) {
case -1:
scaleX = view.getWidth() / win.getWidth();
scaleY = view.getHeight() / win.getHeight();
break;
case 0:
scaleX = scaleY = 1;
break;
default:
scaleX = scaleY = scaleUnits / (double)Units.POINT_DPI;
}
AffineTransform at = new AffineTransform();
at.translate(view.getX(), view.getY());
at.scale(scaleX, scaleY);
at.translate(-win.getX(), -win.getY());
at.translate(-view.getX(), -view.getY());
return at.createTransformedShape(shape);
}
protected BasicStroke getStroke() {
Rectangle2D view = prop.getViewport(), win = prop.getWindow();
float width = (float)(prop.getPenWidth() * view.getWidth() / win.getWidth());
HwmfPenStyle ps = prop.getPenStyle();
int cap = ps.getLineCap().awtFlag;
int join = ps.getLineJoin().awtFlag;
float miterLimit = (float)prop.getPenMiterLimit();
float dashes[] = ps.getLineDash().dashes;
boolean dashAlt = ps.isAlternateDash();
// This value is not an integer index into the dash pattern array.
// Instead, it is a floating-point value that specifies a linear distance.
float dashStart = (dashAlt && dashes.length > 1) ? dashes[0] : 0;
return new BasicStroke(width, cap, join, miterLimit, dashes, dashStart);
}
protected Paint getFill() {
switch (prop.getBrushStyle()) {
default:
case BS_INDEXED:
case BS_PATTERN8X8:
case BS_DIBPATTERN8X8:
case BS_MONOPATTERN:
case BS_NULL: return null;
case BS_PATTERN:
case BS_DIBPATTERN:
case BS_DIBPATTERNPT: return getPatternPaint();
case BS_SOLID: return getSolidFill();
case BS_HATCHED: return getHatchedFill();
}
}
protected Paint getSolidFill() {
return prop.getBrushColor().getColor();
}
protected Paint getHatchedFill() {
int dim = 7, mid = 3;
BufferedImage bi = new BufferedImage(dim, dim, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g = bi.createGraphics();
Color c = (prop.getBkMode() == HwmfBkMode.TRANSPARENT)
? new Color(0, true)
: prop.getBackgroundColor().getColor();
g.setColor(c);
g.fillRect(0, 0, dim, dim);
g.setColor(prop.getBrushColor().getColor());
HwmfHatchStyle h = prop.getBrushHatch();
if (h == HwmfHatchStyle.HS_HORIZONTAL || h == HwmfHatchStyle.HS_CROSS) {
g.drawLine(0, mid, dim, mid);
}
if (h == HwmfHatchStyle.HS_VERTICAL || h == HwmfHatchStyle.HS_CROSS) {
g.drawLine(mid, 0, mid, dim);
}
if (h == HwmfHatchStyle.HS_FDIAGONAL || h == HwmfHatchStyle.HS_DIAGCROSS) {
g.drawLine(0, 0, dim, dim);
}
if (h == HwmfHatchStyle.HS_BDIAGONAL || h == HwmfHatchStyle.HS_DIAGCROSS) {
g.drawLine(0, dim, dim, 0);
}
g.dispose();
return new TexturePaint(bi, new Rectangle(0,0,dim,dim));
}
protected Paint getPatternPaint() {
BufferedImage bi = prop.getBrushBitmap();
return (bi == null) ? null
: new TexturePaint(bi, new Rectangle(0,0,bi.getWidth(),bi.getHeight()));
}
}

View File

@ -18,12 +18,9 @@
package org.apache.poi.hwmf.record;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@ -75,31 +72,45 @@ public class HwmfBitmap16 {
assert(skipSize == 18);
size += 18+LittleEndianConsts.INT_SIZE;
}
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???
}
}
int bytes = (((width * bitsPixel + 15) >> 4) << 1) * height;
assert (bytes == size2);
size += size2;
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;
return size;
}
public BufferedImage getImage() {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
return bi;
}
}

View File

@ -19,12 +19,16 @@ package org.apache.poi.hwmf.record;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@ -59,25 +63,25 @@ public class HwmfBitmapDib {
* Each pixel in the bitmap is represented by a 16-bit value.
* <br/>
* If the Compression field of the BitmapInfoHeader Object is BI_RGB, the Colors field of DIB
* is NULL. Each WORD in the bitmap array represents a single pixel. The relative intensities of
* red, green, and blue are represented with 5 bits for each color component. The value for blue
* is in the least significant 5 bits, followed by 5 bits each for green and red. The most significant
* bit is not used. The color table is used for optimizing colors on palette-based devices, and
* contains the number of entries specified by the ColorUsed field of the BitmapInfoHeader
* is NULL. Each WORD in the bitmap array represents a single pixel. The relative intensities of
* red, green, and blue are represented with 5 bits for each color component. The value for blue
* is in the least significant 5 bits, followed by 5 bits each for green and red. The most significant
* bit is not used. The color table is used for optimizing colors on palette-based devices, and
* contains the number of entries specified by the ColorUsed field of the BitmapInfoHeader
* Object.
* <br/>
* If the Compression field of the BitmapInfoHeader Object is BI_BITFIELDS, the Colors field
* If the Compression field of the BitmapInfoHeader Object is BI_BITFIELDS, the Colors field
* contains three DWORD color masks that specify the red, green, and blue components,
* respectively, of each pixel. Each WORD in the bitmap array represents a single pixel.
* <br/>
* When the Compression field is set to BI_BITFIELDS, bits set in each DWORD mask MUST be
* When the Compression field is set to BI_BITFIELDS, bits set in each DWORD mask MUST be
* contiguous and SHOULD NOT overlap the bits of another mask.
*/
BI_BITCOUNT_4(0x0010),
/**
* The bitmap has a maximum of 2^24 colors, and the Colors field of DIB is
* NULL. Each 3-byte triplet in the bitmap array represents the relative intensities of blue, green,
* and red, respectively, for a pixel. The Colors color table is used for optimizing colors used on
* The bitmap has a maximum of 2^24 colors, and the Colors field of DIB is
* NULL. Each 3-byte triplet in the bitmap array represents the relative intensities of blue, green,
* and red, respectively, for a pixel. The Colors color table is used for optimizing colors used on
* palette-based devices, and MUST contain the number of entries specified by the ColorUsed
* field of the BitmapInfoHeader Object.
*/
@ -85,23 +89,23 @@ public class HwmfBitmapDib {
/**
* The bitmap has a maximum of 2^24 colors.
* <br/>
* If the Compression field of the BitmapInfoHeader Object is set to BI_RGB, the Colors field
* of DIB is set to NULL. Each DWORD in the bitmap array represents the relative intensities of
* blue, green, and red, respectively, for a pixel. The high byte in each DWORD is not used. The
* Colors color table is used for optimizing colors used on palette-based devices, and MUST
* contain the number of entries specified by the ColorUsed field of the BitmapInfoHeader
* If the Compression field of the BitmapInfoHeader Object is set to BI_RGB, the Colors field
* of DIB is set to NULL. Each DWORD in the bitmap array represents the relative intensities of
* blue, green, and red, respectively, for a pixel. The high byte in each DWORD is not used. The
* Colors color table is used for optimizing colors used on palette-based devices, and MUST
* contain the number of entries specified by the ColorUsed field of the BitmapInfoHeader
* Object.
* <br/>
* If the Compression field of the BitmapInfoHeader Object is set to BI_BITFIELDS, the Colors
* field contains three DWORD color masks that specify the red, green, and blue components,
* field contains three DWORD color masks that specify the red, green, and blue components,
* respectively, of each pixel. Each DWORD in the bitmap array represents a single pixel.
* <br/>
* When the Compression field is set to BI_BITFIELDS, bits set in each DWORD mask must be
* contiguous and should not overlap the bits of another mask. All the bits in the pixel do not
* When the Compression field is set to BI_BITFIELDS, bits set in each DWORD mask must be
* contiguous and should not overlap the bits of another mask. All the bits in the pixel do not
* need to be used.
*/
BI_BITCOUNT_6(0x0020);
int flag;
BitCount(int flag) {
this.flag = flag;
@ -139,18 +143,18 @@ public class HwmfBitmapDib {
*/
BI_BITFIELDS(0x0003),
/**
* The image is a JPEG image, as specified in [JFIF]. This value SHOULD only be used in
* certain bitmap operations, such as JPEG pass-through. The application MUST query for the
* pass-through support, since not all devices support JPEG pass-through. Using non-RGB
* bitmaps MAY limit the portability of the metafile to other devices. For instance, display device
* The image is a JPEG image, as specified in [JFIF]. This value SHOULD only be used in
* certain bitmap operations, such as JPEG pass-through. The application MUST query for the
* pass-through support, since not all devices support JPEG pass-through. Using non-RGB
* bitmaps MAY limit the portability of the metafile to other devices. For instance, display device
* contexts generally do not support this pass-through.
*/
BI_JPEG(0x0004),
/**
* The image is a PNG image, as specified in [RFC2083]. This value SHOULD only be
* used certain bitmap operations, such as JPEG/PNG pass-through. The application MUST query
* for the pass-through support, because not all devices support JPEG/PNG pass-through. Using
* non-RGB bitmaps MAY limit the portability of the metafile to other devices. For instance,
* The image is a PNG image, as specified in [RFC2083]. This value SHOULD only be
* used certain bitmap operations, such as JPEG/PNG pass-through. The application MUST query
* for the pass-through support, because not all devices support JPEG/PNG pass-through. Using
* non-RGB bitmaps MAY limit the portability of the metafile to other devices. For instance,
* display device contexts generally do not support this pass-through.
*/
BI_PNG(0x0005),
@ -170,7 +174,7 @@ public class HwmfBitmapDib {
* color indexes.
*/
BI_CMYKRLE4(0x000D);
int flag;
Compression(int flag) {
this.flag = flag;
@ -180,93 +184,113 @@ public class HwmfBitmapDib {
if (c.flag == flag) return c;
}
return null;
}
}
int headerSize;
int headerWidth;
int headerHeight;
int headerPlanes;
BitCount headerBitCount;
Compression headerCompression;
long headerImageSize = -1;
int headerXPelsPerMeter = -1;
int headerYPelsPerMeter = -1;
long headerColorUsed = -1;
long headerColorImportant = -1;
Color colorTable[];
int colorMaskRed=0,colorMaskGreen=0,colorMaskBlue=0;
public int init(LittleEndianInputStream leis) throws IOException {
int size = 0;
size += readHeader(leis);
size += readColors(leis);
int size2;
switch (headerBitCount) {
default:
case BI_BITCOUNT_0:
throw new RuntimeException("JPG and PNG formats aren't supported yet.");
case BI_BITCOUNT_1:
case BI_BITCOUNT_2:
case BI_BITCOUNT_3:
size2 = readBitmapIndexed(leis);
break;
case BI_BITCOUNT_4:
case BI_BITCOUNT_5:
case BI_BITCOUNT_6:
size2 = readBitmapDirect(leis);
break;
}
}
private static final int BMP_HEADER_SIZE = 14;
private int headerSize;
private int headerWidth;
private int headerHeight;
private int headerPlanes;
private BitCount headerBitCount;
private Compression headerCompression;
private long headerImageSize = -1;
@SuppressWarnings("unused")
private int headerXPelsPerMeter = -1;
@SuppressWarnings("unused")
private int headerYPelsPerMeter = -1;
private long headerColorUsed = -1;
@SuppressWarnings("unused")
private long headerColorImportant = -1;
@SuppressWarnings("unused")
private Color colorTable[];
@SuppressWarnings("unused")
private int colorMaskRed=0,colorMaskGreen=0,colorMaskBlue=0;
// size of header and color table, for start of image data calculation
private int introSize;
private byte imageData[];
public int init(LittleEndianInputStream leis, int recordSize) throws IOException {
leis.mark(10000);
assert( headerSize != 0x0C || ((((headerWidth * headerPlanes * headerBitCount.flag + 31) & ~31) / 8) * Math.abs(headerHeight)) == size2);
assert ( headerSize == 0x0C || headerImageSize == size2 );
// need to read the header to calculate start of bitmap data correct
introSize = readHeader(leis);
assert(introSize == headerSize);
introSize += readColors(leis);
assert(introSize < 10000);
int fileSize = (headerImageSize != 0) ? (int)(introSize+headerImageSize) : recordSize;
size += size2;
imageData = new byte[fileSize];
leis.reset();
leis.read(imageData, 0, fileSize);
return size;
assert( headerSize != 0x0C || ((((headerWidth * headerPlanes * headerBitCount.flag + 31) & ~31) / 8) * Math.abs(headerHeight)) == headerImageSize);
return fileSize;
}
protected int readHeader(LittleEndianInputStream leis) throws IOException {
int size = 0;
/**
* DIBHeaderInfo (variable): Either a BitmapCoreHeader Object or a
* BitmapInfoHeader Object that specifies information about the image.
*
*
* The first 32 bits of this field is the HeaderSize value.
* If it is 0x0000000C, then this is a BitmapCoreHeader; otherwise, this is a BitmapInfoHeader.
*/
headerSize = leis.readInt();
size += LittleEndianConsts.INT_SIZE;
// BitmapCoreHeader
// A 16-bit unsigned integer that defines the width of the DIB, in pixels.
headerWidth = leis.readUShort();
// A 16-bit unsigned integer that defines the height of the DIB, in pixels.
headerHeight = leis.readUShort();
// A 16-bit unsigned integer that defines the number of planes for the target
// device. This value MUST be 0x0001.
headerPlanes = leis.readUShort();
// A 16-bit unsigned integer that defines the format of each pixel, and the
// maximum number of colors in the DIB.
headerBitCount = BitCount.valueOf(leis.readUShort());
size += 4*LittleEndianConsts.SHORT_SIZE;
if (headerSize > 0x0C) {
if (headerSize == 0x0C) {
// BitmapCoreHeader
// A 16-bit unsigned integer that defines the width of the DIB, in pixels.
headerWidth = leis.readUShort();
// A 16-bit unsigned integer that defines the height of the DIB, in pixels.
headerHeight = leis.readUShort();
// A 16-bit unsigned integer that defines the number of planes for the target
// device. This value MUST be 0x0001.
headerPlanes = leis.readUShort();
// A 16-bit unsigned integer that defines the format of each pixel, and the
// maximum number of colors in the DIB.
headerBitCount = BitCount.valueOf(leis.readUShort());
size += 4*LittleEndianConsts.SHORT_SIZE;
} else {
// BitmapInfoHeader
// A 32-bit unsigned integer that defines the compression mode of the
// DIB.
// A 32-bit signed integer that defines the width of the DIB, in pixels.
// This value MUST be positive.
// This field SHOULD specify the width of the decompressed image file,
// if the Compression value specifies JPEG or PNG format.
headerWidth = leis.readInt();
// A 32-bit signed integer that defines the height of the DIB, in pixels.
// This value MUST NOT be zero.
// - If this value is positive, the DIB is a bottom-up bitmap,
// and its origin is the lower-left corner.
// This field SHOULD specify the height of the decompressed image file,
// if the Compression value specifies JPEG or PNG format.
// - If this value is negative, the DIB is a top-down bitmap,
// and its origin is the upper-left corner. Top-down bitmaps do not support compression.
headerHeight = leis.readInt();
// A 16-bit unsigned integer that defines the number of planes for the target
// device. This value MUST be 0x0001.
headerPlanes = leis.readUShort();
// A 16-bit unsigned integer that defines the format of each pixel, and the
// maximum number of colors in the DIB.
headerBitCount = BitCount.valueOf(leis.readUShort());
// A 32-bit unsigned integer that defines the compression mode of the DIB.
// This value MUST NOT specify a compressed format if the DIB is a top-down bitmap,
// as indicated by the Height value.
headerCompression = Compression.valueOf((int)leis.readUInt());
// A 32-bit unsigned integer that defines the size, in bytes, of the image.
// If the Compression value is BI_RGB, this value SHOULD be zero and MUST be ignored.
// If the Compression value is BI_JPEG or BI_PNG, this value MUST specify the size of the JPEG
// If the Compression value is BI_JPEG or BI_PNG, this value MUST specify the size of the JPEG
// or PNG image buffer, respectively.
headerImageSize = leis.readUInt();
// A 32-bit signed integer that defines the horizontal resolution,
// A 32-bit signed integer that defines the horizontal resolution,
// in pixels-per-meter, of the target device for the DIB.
headerXPelsPerMeter = leis.readInt();
// A 32-bit signed integer that defines the vertical resolution,
@ -278,8 +302,9 @@ public class HwmfBitmapDib {
// A 32-bit unsigned integer that defines the number of color indexes that are
// required for displaying the DIB. If this value is zero, all color indexes are required.
headerColorImportant = leis.readUInt();
size += 6*LittleEndianConsts.INT_SIZE;
size += 8*LittleEndianConsts.INT_SIZE+2*LittleEndianConsts.SHORT_SIZE;
}
assert(size == headerSize);
return size;
}
@ -297,7 +322,7 @@ public class HwmfBitmapDib {
return readRGBQuad(leis, 16);
case BI_BITCOUNT_3:
// 256 colors
return readRGBQuad(leis, 256);
return readRGBQuad(leis, (int)headerColorUsed);
case BI_BITCOUNT_5:
colorMaskRed=0xFF;
colorMaskGreen=0xFF;
@ -316,7 +341,7 @@ public class HwmfBitmapDib {
colorMaskRed = leis.readInt();
return 3*LittleEndianConsts.INT_SIZE;
}
case BI_BITCOUNT_6:
case BI_BITCOUNT_6:
if (headerCompression == Compression.BI_RGB) {
colorMaskBlue = colorMaskGreen = colorMaskRed = 0xFF;
return 0;
@ -329,7 +354,7 @@ public class HwmfBitmapDib {
}
}
}
protected int readRGBQuad(LittleEndianInputStream leis, int count) throws IOException {
int size = 0;
List<Color> colorList = new ArrayList<Color>();
@ -346,96 +371,32 @@ public class HwmfBitmapDib {
colorTable = colorList.toArray(new Color[colorList.size()]);
return size;
}
protected int readBitmapIndexed(LittleEndianInputStream leis) throws IOException {
assert(colorTable != null);
byte r[] = new byte[colorTable.length];
byte g[] = new byte[colorTable.length];
byte b[] = new byte[colorTable.length];
for (int i=0; i<colorTable.length; i++) {
r[i] = (byte)colorTable[i].getRed();
g[i] = (byte)colorTable[i].getGreen();
b[i] = (byte)colorTable[i].getBlue();
public BufferedImage getImage() {
if (imageData == null) {
throw new RecordFormatException("bitmap not initialized ... need to call init() before");
}
int bits = 32-Integer.numberOfLeadingZeros(colorTable.length);
IndexColorModel cm = new IndexColorModel(bits,colorTable.length,r,g,b);
// create the image data and leave the parsing to the ImageIO api
byte buf[] = new byte[BMP_HEADER_SIZE+imageData.length];
// 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));
// 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);
BufferedImage bi = new BufferedImage(headerWidth, headerHeight, BufferedImage.TYPE_BYTE_INDEXED, cm);
WritableRaster wr = bi.getRaster();
System.arraycopy(imageData, 0, buf, BMP_HEADER_SIZE, imageData.length);
int pixelCount = headerWidth*headerHeight;
int size = 0;
for (int pixel=0; pixel<pixelCount; size++) {
int v = leis.readUByte();
switch (headerBitCount) {
default:
throw new RuntimeException("invalid bitcount for indexed image");
case BI_BITCOUNT_1:
for (int j=0; j<8 && pixel<pixelCount; j++,pixel++) {
wr.setSample(pixel/headerWidth,pixel%headerWidth,0,(v>>(7-j))&1);
}
break;
case BI_BITCOUNT_2:
wr.setSample(pixel/headerWidth, pixel%headerWidth, 0, (v>>4)&15);
pixel++;
if (pixel<pixelCount) {
wr.setSample(pixel/headerWidth, pixel%headerWidth, 0, v&15);
pixel++;
}
break;
case BI_BITCOUNT_3:
wr.setSample(pixel/headerWidth, pixel%headerWidth, 0, v);
pixel++;
break;
}
try {
return ImageIO.read(new ByteArrayInputStream(buf));
} catch (IOException e) {
// ... shouldn't happen
throw new RecordFormatException("invalid bitmap data", e);
}
return size;
}
protected int readBitmapDirect(LittleEndianInputStream leis) throws IOException {
assert(colorTable == null);
BufferedImage bi = new BufferedImage(headerWidth, headerHeight, BufferedImage.TYPE_INT_RGB);
WritableRaster wr = bi.getRaster();
int bitShiftRed=0,bitShiftGreen=0,bitShiftBlue=0;
if (headerCompression == Compression.BI_BITFIELDS) {
bitShiftGreen = 32-Integer.numberOfLeadingZeros(this.colorMaskBlue);
bitShiftRed = 32-Integer.numberOfLeadingZeros(this.colorMaskGreen);
}
int pixelCount = headerWidth*headerHeight;
int size = 0;
int rgb[] = new int[3];
for (int pixel=0; pixel<pixelCount; pixel++) {
int v;
switch (headerBitCount) {
default:
throw new RuntimeException("invalid bitcount for indexed image");
case BI_BITCOUNT_4:
v = leis.readUShort();
rgb[0] = (v & colorMaskRed) >> bitShiftRed;
rgb[1] = (v & colorMaskGreen) >> bitShiftGreen;
rgb[2] = (v & colorMaskBlue) >> bitShiftBlue;
size += LittleEndianConsts.SHORT_SIZE;
break;
case BI_BITCOUNT_5:
rgb[2] = leis.readUByte();
rgb[1] = leis.readUByte();
rgb[0] = leis.readUByte();
size += 3*LittleEndianConsts.BYTE_SIZE;
break;
case BI_BITCOUNT_6:
v = leis.readInt();
rgb[0] = (v & colorMaskRed) >> bitShiftRed;
rgb[1] = (v & colorMaskGreen) >> bitShiftGreen;
rgb[2] = (v & colorMaskBlue) >> bitShiftBlue;
size += LittleEndianConsts.INT_SIZE;
break;
}
wr.setPixel(pixel/headerWidth,pixel%headerWidth,rgb);
}
return size;
}
}

View File

@ -23,15 +23,21 @@ import java.io.IOException;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
/**
* A 32-bit ColorRef Object that defines the color value.
* Red (1 byte): An 8-bit unsigned integer that defines the relative intensity of red.
* Green (1 byte): An 8-bit unsigned integer that defines the relative intensity of green.
* Blue (1 byte): An 8-bit unsigned integer that defines the relative intensity of blue.
* Reserved (1 byte): An 8-bit unsigned integer that MUST be 0x00.
*/
public class HwmfColorRef {
/**
* A 32-bit ColorRef Object that defines the color value.
* Red (1 byte): An 8-bit unsigned integer that defines the relative intensity of red.
* Green (1 byte): An 8-bit unsigned integer that defines the relative intensity of green.
* Blue (1 byte): An 8-bit unsigned integer that defines the relative intensity of blue.
* Reserved (1 byte): An 8-bit unsigned integer that MUST be 0x00.
*/
Color colorRef;
private Color colorRef = Color.BLACK;
public HwmfColorRef() {}
public HwmfColorRef(Color colorRef) {
this.colorRef = colorRef;
}
public int init(LittleEndianInputStream leis) throws IOException {
int red = leis.readUByte();
@ -44,4 +50,7 @@ public class HwmfColorRef {
return 4*LittleEndianConsts.BYTE_SIZE;
}
public Color getColor() {
return colorRef;
}
}

View File

@ -17,8 +17,13 @@
package org.apache.poi.hwmf.record;
import java.awt.Polygon;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.io.IOException;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@ -28,102 +33,140 @@ public class HwmfDraw {
* point.
*/
public static class WmfMoveTo implements HwmfRecord {
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units.
*/
int y;
private int y;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units.
*/
int x;
private int x;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.moveTo;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
y = leis.readShort();
x = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.getProperties().setLocation(x, y);
}
}
/**
* The META_LINETO record draws a line from the drawing position that is defined in the playback
* device context up to, but not including, the specified point.
*/
public static class WmfLineTo implements HwmfRecord {
/**
* A 16-bit signed integer that defines the vertical component of the drawing
* destination position, in logical units.
*/
int y;
private int y;
/**
* A 16-bit signed integer that defines the horizontal component of the drawing
* destination position, in logical units.
*/
int x;
private int x;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.lineTo;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
y = leis.readShort();
x = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
Point2D start = ctx.getProperties().getLocation();
Line2D line = new Line2D.Double(start.getX(), start.getY(), x, y);
ctx.draw(line);
ctx.getProperties().setLocation(x, y);
}
}
/**
* The META_POLYGON record paints a polygon consisting of two or more vertices connected by
* straight lines. The polygon is outlined by using the pen and filled by using the brush and polygon fill
* mode that are defined in the playback device context.
*/
public static class WmfPolygon implements HwmfRecord {
/**
* A 16-bit signed integer that defines the number of points in the array.
*/
int numberofPoints;
private int numberofPoints;
short xPoints[], yPoints[];
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.polygon;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
numberofPoints = leis.readShort();
xPoints = new short[numberofPoints];
yPoints = new short[numberofPoints];
for (int i=0; i<numberofPoints; i++) {
// A 16-bit signed integer that defines the horizontal (x) coordinate of the point.
xPoints[i] = leis.readShort();
// A 16-bit signed integer that defines the vertical (y) coordinate of the point.
yPoints[i] = leis.readShort();
}
return LittleEndianConsts.SHORT_SIZE+numberofPoints*LittleEndianConsts.INT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.fill(getShape());
}
protected Polygon getShape() {
Polygon polygon = new Polygon();
for(int i = 0; i < numberofPoints; i++) {
polygon.addPoint(xPoints[i], yPoints[i]);
}
return polygon;
}
}
/**
* The META_POLYLINE record draws a series of line segments by connecting the points in the
* specified array.
*/
public static class WmfPolyline extends WmfPolygon {
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.polyline;
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.draw(getShape());
}
}
/**
* The META_ELLIPSE record draws an ellipse. The center of the ellipse is the center of the specified
* bounding rectangle. The ellipse is outlined by using the pen and is filled by using the brush; these
@ -134,27 +177,29 @@ public class HwmfDraw {
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* the lower-right corner of the bounding rectangle.
*/
int bottomRect;
private int bottomRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the lower-right corner of the bounding rectangle.
*/
int rightRect;
private int rightRect;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the bounding rectangle.
*/
int topRect;
private int topRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the upper-left corner of the bounding rectangle.
*/
int leftRect;
private int leftRect;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.ellipse;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
bottomRect = leis.readShort();
rightRect = leis.readShort();
@ -162,6 +207,11 @@ public class HwmfDraw {
leftRect = leis.readShort();
return 4*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
@ -173,27 +223,29 @@ public class HwmfDraw {
* A 16-bit unsigned integer used to index into the WMF Object Table to get
* the region to be framed.
*/
int region;
private int region;
/**
* A 16-bit unsigned integer used to index into the WMF Object Table to get the
* Brush to use for filling the region.
*/
int brush;
private int brush;
/**
* A 16-bit signed integer that defines the height, in logical units, of the
* region frame.
*/
int height;
private int height;
/**
* A 16-bit signed integer that defines the width, in logical units, of the
* region frame.
*/
int width;
private int width;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.frameRegion;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
region = leis.readUShort();
brush = leis.readUShort();
@ -201,6 +253,11 @@ public class HwmfDraw {
width = leis.readShort();
return 4*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -209,33 +266,35 @@ public class HwmfDraw {
* device context. The polygons drawn by this function can overlap.
*/
public static class WmfPolyPolygon implements HwmfRecord {
/**
* A 16-bit unsigned integer that defines the number of polygons in the object.
*/
int numberOfPolygons;
private int numberOfPolygons;
/**
* A NumberOfPolygons array of 16-bit unsigned integers that define the number of
* points for each polygon in the object.
*/
int pointsPerPolygon[];
/**
* An array of 16-bit unsigned integers that define the coordinates of the polygons.
*/
int xPoints[][];
private int pointsPerPolygon[];
/**
* An array of 16-bit unsigned integers that define the coordinates of the polygons.
*/
int yPoints[][];
private int xPoints[][];
/**
* An array of 16-bit unsigned integers that define the coordinates of the polygons.
*/
private int yPoints[][];
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.polyPolygon;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
// see http://secunia.com/gfx/pdf/SA31675_BA.pdf ;)
numberOfPolygons = leis.readUShort();
@ -244,26 +303,31 @@ public class HwmfDraw {
yPoints = new int[numberOfPolygons][];
int size = LittleEndianConsts.SHORT_SIZE;
for (int i=0; i<numberOfPolygons; i++) {
pointsPerPolygon[i] = leis.readUShort();
size += LittleEndianConsts.SHORT_SIZE;
}
for (int i=0; i<numberOfPolygons; i++) {
xPoints[i] = new int[pointsPerPolygon[i]];
yPoints[i] = new int[pointsPerPolygon[i]];
for (int j=0; j<pointsPerPolygon[i]; j++) {
xPoints[i][j] = leis.readUShort();
yPoints[i][j] = leis.readUShort();
size += 2*LittleEndianConsts.SHORT_SIZE;
}
}
return size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -275,27 +339,29 @@ public class HwmfDraw {
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* the lower-right corner of the rectangle.
*/
int bottomRect;
private int bottomRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the lower-right corner of the rectangle.
*/
int rightRect;
private int rightRect;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the rectangle.
*/
int topRect;
private int topRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the upper-left corner of the rectangle.
*/
int leftRect;
private int leftRect;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.frameRegion;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
bottomRect = leis.readShort();
rightRect = leis.readShort();
@ -303,6 +369,11 @@ public class HwmfDraw {
leftRect = leis.readShort();
return 4*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -313,25 +384,27 @@ public class HwmfDraw {
/**
* A ColorRef Object that defines the color value.
*/
HwmfColorRef colorRef;
HwmfColorRef colorRef;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the point
* to be set.
*/
int y;
private int y;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the point
* to be set.
*/
int x;
private int x;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setPixel;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
colorRef = new HwmfColorRef();
int size = colorRef.init(leis);
@ -339,6 +412,11 @@ public class HwmfDraw {
x = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE+size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -350,43 +428,45 @@ public class HwmfDraw {
* A 16-bit signed integer that defines the height, in logical coordinates, of the
* ellipse used to draw the rounded corners.
*/
int height;
private int height;
/**
* A 16-bit signed integer that defines the width, in logical coordinates, of the
* ellipse used to draw the rounded corners.
*/
int width;
private int width;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* the lower-right corner of the rectangle.
*/
int bottomRect;
private int bottomRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the lower-right corner of the rectangle.
*/
int rightRect;
private int rightRect;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the rectangle.
*/
int topRect;
private int topRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the upper-left corner of the rectangle.
*/
int leftRect;
private int leftRect;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.roundRect;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
height = leis.readShort();
width = leis.readShort();
@ -396,8 +476,13 @@ public class HwmfDraw {
leftRect = leis.readShort();
return 6*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -410,47 +495,49 @@ public class HwmfDraw {
* A 16-bit signed integer that defines the y-coordinate, in logical
* coordinates, of the endpoint of the second radial.
*/
int yRadial2;
private int yRadial2;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical
* coordinates, of the endpoint of the second radial.
*/
int xRadial2;
private int xRadial2;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical
* A 16-bit signed integer that defines the y-coordinate, in logical
* coordinates, of the endpoint of the first radial.
*/
int yRadial1;
private int yRadial1;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical
* A 16-bit signed integer that defines the x-coordinate, in logical
* coordinates, of the endpoint of the first radial.
*/
int xRadial1;
private int xRadial1;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* the lower-right corner of the bounding rectangle.
*/
int bottomRect;
private int bottomRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the lower-right corner of the bounding rectangle.
*/
int rightRect;
private int rightRect;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the bounding rectangle.
*/
int topRect;
private int topRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the upper-left corner of the bounding rectangle.
*/
int leftRect;
private int leftRect;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.pie;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
yRadial2 = leis.readShort();
xRadial2 = leis.readShort();
@ -462,6 +549,12 @@ public class HwmfDraw {
leftRect = leis.readShort();
return 8*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -472,47 +565,49 @@ public class HwmfDraw {
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* the ending point of the radial line defining the ending point of the arc.
*/
int yEndArc;
private int yEndArc;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the ending point of the radial line defining the ending point of the arc.
*/
int xEndArc;
private int xEndArc;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* the ending point of the radial line defining the starting point of the arc.
*/
int yStartArc;
private int yStartArc;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the ending point of the radial line defining the starting point of the arc.
*/
int xStartArc;
private int xStartArc;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* the lower-right corner of the bounding rectangle.
*/
int bottomRect;
private int bottomRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the lower-right corner of the bounding rectangle.
*/
int rightRect;
private int rightRect;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the bounding rectangle.
*/
int topRect;
private int topRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the upper-left corner of the bounding rectangle.
*/
int leftRect;
private int leftRect;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.arc;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
yEndArc = leis.readShort();
xEndArc = leis.readShort();
@ -524,6 +619,11 @@ public class HwmfDraw {
leftRect = leis.readShort();
return 8*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -533,51 +633,53 @@ public class HwmfDraw {
*/
public static class WmfChord implements HwmfRecord {
/**
* A 16-bit signed integer that defines the y-coordinate, in logical
* A 16-bit signed integer that defines the y-coordinate, in logical
* coordinates, of the endpoint of the second radial.
*/
int yRadial2;
private int yRadial2;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical
* A 16-bit signed integer that defines the x-coordinate, in logical
* coordinates, of the endpoint of the second radial.
*/
int xRadial2;
private int xRadial2;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical
* A 16-bit signed integer that defines the y-coordinate, in logical
* coordinates, of the endpoint of the first radial.
*/
int yRadial1;
private int yRadial1;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical
* A 16-bit signed integer that defines the x-coordinate, in logical
* coordinates, of the endpoint of the first radial.
*/
int xRadial1;
private int xRadial1;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* A 16-bit signed integer that defines the y-coordinate, in logical units, of
* the lower-right corner of the bounding rectangle.
*/
int bottomRect;
private int bottomRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the lower-right corner of the bounding rectangle.
*/
int rightRect;
private int rightRect;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the bounding rectangle.
*/
int topRect;
private int topRect;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* A 16-bit signed integer that defines the x-coordinate, in logical units, of
* the upper-left corner of the bounding rectangle.
*/
int leftRect;
private int leftRect;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.chord;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
yRadial2 = leis.readShort();
xRadial2 = leis.readShort();
@ -589,31 +691,43 @@ public class HwmfDraw {
leftRect = leis.readShort();
return 8*LittleEndianConsts.SHORT_SIZE;
}
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
* The META_SELECTOBJECT record specifies a graphics object for the playback device context. The
* new object replaces the previous object of the same type, unless if the previous object is a palette
* object. If the previous object is a palette object, then the META_SELECTPALETTE record must be
* used instead of the META_SELECTOBJECT record, as the META_SELECTOBJECT record does not
* The META_SELECTOBJECT record specifies a graphics object for the playback device context. The
* new object replaces the previous object of the same type, unless if the previous object is a palette
* object. If the previous object is a palette object, then the META_SELECTPALETTE record must be
* used instead of the META_SELECTOBJECT record, as the META_SELECTOBJECT record does not
* support replacing the palette object type.
*/
public static class WmfSelectObject implements HwmfRecord {
/**
* A 16-bit unsigned integer used to index into the WMF Object Table to
* get the object to be selected.
*/
int objectIndex;
private int objectIndex;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.selectObject;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
objectIndex = leis.readUShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
}

View File

@ -19,6 +19,8 @@ package org.apache.poi.hwmf.record;
import java.io.IOException;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@ -28,21 +30,23 @@ public class HwmfEscape implements HwmfRecord {
* A 16-bit unsigned integer that defines the escape function. The
* value MUST be from the MetafileEscapes enumeration.
*/
int escapeFunction;
private int escapeFunction;
/**
* A 16-bit unsigned integer that specifies the size, in bytes, of the
* EscapeData field.
*/
int byteCount;
private int byteCount;
/**
* An array of bytes of size ByteCount.
*/
byte escapeData[];
private byte escapeData[];
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.escape;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
escapeFunction = leis.readUShort();
byteCount = leis.readUShort();
@ -50,4 +54,16 @@ public class HwmfEscape implements HwmfRecord {
leis.read(escapeData);
return 2*LittleEndianConsts.SHORT_SIZE+byteCount;
}
@Override
public void draw(HwmfGraphics ctx) {
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("escape - function: "+escapeFunction+"\n");
sb.append(HexDump.dump(escapeData, 0, 0));
return sb.toString();
}
}

View File

@ -17,12 +17,26 @@
package org.apache.poi.hwmf.record;
import java.awt.geom.Path2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
public class HwmfFill {
/**
* A record which contains an image (to be extracted)
*/
public interface HwmfImageRecord {
BufferedImage getImage();
}
/**
* The META_FILLREGION record fills a region using a specified brush.
*/
@ -32,23 +46,30 @@ public class HwmfFill {
* A 16-bit unsigned integer used to index into the WMF Object Table to get
* the region to be filled.
*/
int region;
private int region;
/**
* A 16-bit unsigned integer used to index into the WMF Object Table to get the
* brush to use for filling the region.
*/
int brush;
private int brush;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.fillRegion;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
region = leis.readUShort();
brush = leis.readUShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -71,6 +92,11 @@ public class HwmfFill {
region = leis.readUShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
@ -83,23 +109,25 @@ public class HwmfFill {
/**
* A 32-bit ColorRef Object that defines the color value.
*/
HwmfColorRef colorRef;
private HwmfColorRef colorRef;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* point where filling is to start.
*/
int yStart;
private int yStart;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* point where filling is to start.
*/
int xStart;
private int xStart;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.floodFill;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
colorRef = new HwmfColorRef();
int size = colorRef.init(leis);
@ -107,6 +135,11 @@ public class HwmfFill {
xStart = leis.readShort();
return size+2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -114,21 +147,57 @@ public class HwmfFill {
* graphics operations that fill polygons.
*/
public static class WmfSetPolyfillMode implements HwmfRecord {
/**
* A 16-bit unsigned integer that defines polygon fill mode.
* This MUST be one of the values: ALTERNATE = 0x0001, WINDING = 0x0002
*/
public enum HwmfPolyfillMode {
/**
* Selects alternate mode (fills the area between odd-numbered and
* even-numbered polygon sides on each scan line).
*/
ALTERNATE(0x0001, Path2D.WIND_EVEN_ODD),
/**
* Selects winding mode (fills any region with a nonzero winding value).
*/
WINDING(0x0002, Path2D.WIND_NON_ZERO);
public int wmfFlag;
public int awtFlag;
HwmfPolyfillMode(int wmfFlag, int awtFlag) {
this.wmfFlag = wmfFlag;
this.awtFlag = awtFlag;
}
static HwmfPolyfillMode valueOf(int wmfFlag) {
for (HwmfPolyfillMode pm : values()) {
if (pm.wmfFlag == wmfFlag) return pm;
}
return null;
}
}
/**
* A 16-bit unsigned integer that defines polygon fill mode.
* This MUST be one of the values: ALTERNATE = 0x0001, WINDING = 0x0002
*/
int polyFillMode;
private HwmfPolyfillMode polyfillMode;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setPolyFillMode;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
polyFillMode = leis.readUShort();
polyfillMode = HwmfPolyfillMode.valueOf(leis.readUShort());
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.getProperties().setPolyfillMode(polyfillMode);
}
}
@ -151,29 +220,31 @@ public class HwmfFill {
* Filling continues outward in all directions as long as the color is encountered.
* This style is useful for filling areas with multicolored boundaries.
*/
int mode;
private int mode;
/**
* A 32-bit ColorRef Object that defines the color value.
*/
HwmfColorRef colorRef;
private HwmfColorRef colorRef;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the point
* to be set.
*/
int y;
private int y;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the point
* to be set.
*/
int x;
private int x;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.extFloodFill;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
mode = leis.readUShort();
colorRef = new HwmfColorRef();
@ -182,6 +253,11 @@ public class HwmfFill {
x = leis.readShort();
return size+3*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -193,16 +269,23 @@ public class HwmfFill {
* A 16-bit unsigned integer used to index into the WMF Object Table to get
* the region to be inverted.
*/
int region;
private int region;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.invertRegion;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
region = leis.readUShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
@ -217,39 +300,41 @@ public class HwmfFill {
* A 32-bit unsigned integer that defines the raster operation code.
* This code MUST be one of the values in the Ternary Raster Operation enumeration table.
*/
HwmfTernaryRasterOp rasterOperation;
private HwmfTernaryRasterOp rasterOperation;
/**
* A 16-bit signed integer that defines the height, in logical units, of the rectangle.
*/
int height;
private int height;
/**
* A 16-bit signed integer that defines the width, in logical units, of the rectangle.
*/
int width;
private int width;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the rectangle to be filled.
*/
int yLeft;
private int yLeft;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* upper-left corner of the rectangle to be filled.
*/
int xLeft;
private int xLeft;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.patBlt;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
int rasterOpIndex = leis.readUShort();
int rasterOpCode = leis.readUShort();
int rasterOpIndex = leis.readUShort();
rasterOperation = HwmfTernaryRasterOp.fromOpIndex(rasterOpIndex);
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
assert(rasterOpCode == rasterOperation.opCode);
height = leis.readShort();
@ -259,6 +344,11 @@ public class HwmfFill {
return 6*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -269,44 +359,44 @@ public class HwmfFill {
* in the playback device context, and the destination pixels are to be combined to form the new
* image. This code MUST be one of the values in the Ternary Raster Operation Enumeration
*/
HwmfTernaryRasterOp rasterOperation;
private HwmfTernaryRasterOp rasterOperation;
/**
* A 16-bit signed integer that defines the height, in logical units, of the source rectangle.
*/
int srcHeight;
private int srcHeight;
/**
* A 16-bit signed integer that defines the width, in logical units, of the source rectangle.
*/
int srcWidth;
private int srcWidth;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the upper-left corner
* of the source rectangle.
*/
int ySrc;
private int ySrc;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the upper-left corner
* of the source rectangle.
*/
int xSrc;
private int xSrc;
/**
* A 16-bit signed integer that defines the height, in logical units, of the destination rectangle.
*/
int destHeight;
private int destHeight;
/**
* A 16-bit signed integer that defines the width, in logical units, of the destination rectangle.
*/
int destWidth;
private int destWidth;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the upper-left
* corner of the destination rectangle.
*/
int yDest;
private int yDest;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the upper-left
* corner of the destination rectangle.
*/
int xDest;
private int xDest;
/**
* A variable-sized Bitmap16 Object that defines source image content.
@ -314,19 +404,21 @@ public class HwmfFill {
*/
HwmfBitmap16 target;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.stretchBlt;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3));
int size = 0;
int rasterOpIndex = leis.readUShort();
int rasterOpCode = leis.readUShort();
int rasterOpIndex = leis.readUShort();
rasterOperation = HwmfTernaryRasterOp.fromOpIndex(rasterOpIndex);
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
assert(rasterOpCode == rasterOperation.opCode);
srcHeight = leis.readShort();
@ -351,6 +443,11 @@ public class HwmfFill {
return size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -360,13 +457,13 @@ public class HwmfFill {
* The source of the color data is a DIB, and the destination of the transfer is
* the current output region in the playback device context.
*/
public static class WmfStretchDib implements HwmfRecord {
public static class WmfStretchDib implements HwmfRecord, HwmfImageRecord {
/**
* A 32-bit unsigned integer that defines how the source pixels, the current brush in
* the playback device context, and the destination pixels are to be combined to
* form the new image.
*/
HwmfTernaryRasterOp rasterOperation;
private HwmfTernaryRasterOp rasterOperation;
/**
* A 16-bit unsigned integer that defines whether the Colors field of the
@ -376,63 +473,65 @@ public class HwmfFill {
* DIB_PAL_COLORS = 0x0001,
* DIB_PAL_INDICES = 0x0002
*/
int colorUsage;
private int colorUsage;
/**
* A 16-bit signed integer that defines the height, in logical units, of the
* source rectangle.
*/
int srcHeight;
private int srcHeight;
/**
* A 16-bit signed integer that defines the width, in logical units, of the
* source rectangle.
*/
int srcWidth;
private int srcWidth;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* source rectangle.
*/
int ySrc;
private int ySrc;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* source rectangle.
*/
int xSrc;
private int xSrc;
/**
* A 16-bit signed integer that defines the height, in logical units, of the
* destination rectangle.
*/
int destHeight;
private int destHeight;
/**
* A 16-bit signed integer that defines the width, in logical units, of the
* destination rectangle.
*/
int destWidth;
private int destWidth;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the destination rectangle.
*/
int yDst;
private int yDst;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* upper-left corner of the destination rectangle.
*/
int xDst;
private int xDst;
/**
* A variable-sized DeviceIndependentBitmap Object (section 2.2.2.9) that is the
* source of the color data.
*/
HwmfBitmapDib dib;
private HwmfBitmapDib dib;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.stretchDib;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
int rasterOpIndex = leis.readUShort();
int rasterOpCode = leis.readUShort();
int rasterOpIndex = leis.readUShort();
rasterOperation = HwmfTernaryRasterOp.fromOpIndex(rasterOpIndex);
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
assert(rasterOpCode == rasterOperation.opCode);
colorUsage = leis.readUShort();
@ -447,9 +546,20 @@ public class HwmfFill {
int size = 11*LittleEndianConsts.SHORT_SIZE;
dib = new HwmfBitmapDib();
size += dib.init(leis);
size += dib.init(leis, (int)(recordSize-6-size));
return size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
@Override
public BufferedImage getImage() {
return dib.getImage();
}
}
public static class WmfBitBlt implements HwmfRecord {
@ -458,57 +568,59 @@ public class HwmfFill {
* A 32-bit unsigned integer that defines how the source pixels, the current brush in the playback
* device context, and the destination pixels are to be combined to form the new image.
*/
HwmfTernaryRasterOp rasterOperation;
private HwmfTernaryRasterOp rasterOperation;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the upper-left corner
of the source rectangle.
*/
int ySrc;
private int ySrc;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the upper-left corner
of the source rectangle.
*/
int xSrc;
private int xSrc;
/**
* A 16-bit signed integer that defines the height, in logical units, of the source and
destination rectangles.
*/
int height;
private int height;
/**
* A 16-bit signed integer that defines the width, in logical units, of the source and destination
rectangles.
*/
int width;
private int width;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the upper-left
corner of the destination rectangle.
*/
int yDest;
private int yDest;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the upper-left
corner of the destination rectangle.
*/
int xDest;
private int xDest;
/**
* A variable-sized Bitmap16 Object that defines source image content.
* This object MUST be specified, even if the raster operation does not require a source.
*/
HwmfBitmap16 target;
private HwmfBitmap16 target;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.bitBlt;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3));
int size = 0;
int rasterOpIndex = leis.readUShort();
int rasterOpCode = leis.readUShort();
int rasterOpIndex = leis.readUShort();
rasterOperation = HwmfTernaryRasterOp.fromOpIndex(rasterOpIndex);
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
assert(rasterOpCode == rasterOperation.opCode);
ySrc = leis.readShort();
@ -535,6 +647,11 @@ public class HwmfFill {
return size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
@ -543,7 +660,7 @@ public class HwmfFill {
* using deviceindependent color data.
* The source of the color data is a DIB
*/
public static class WmfSetDibToDev implements HwmfRecord {
public static class WmfSetDibToDev implements HwmfRecord, HwmfImageRecord {
/**
* A 16-bit unsigned integer that defines whether the Colors field of the
@ -553,55 +670,57 @@ public class HwmfFill {
* DIB_PAL_COLORS = 0x0001,
* DIB_PAL_INDICES = 0x0002
*/
int colorUsage;
private int colorUsage;
/**
* A 16-bit unsigned integer that defines the number of scan lines in the source.
*/
int scanCount;
private int scanCount;
/**
* A 16-bit unsigned integer that defines the starting scan line in the source.
*/
int startScan;
private int startScan;
/**
* A 16-bit unsigned integer that defines the y-coordinate, in logical units, of the
* source rectangle.
*/
int yDib;
private int yDib;
/**
* A 16-bit unsigned integer that defines the x-coordinate, in logical units, of the
* source rectangle.
*/
int xDib;
private int xDib;
/**
* A 16-bit unsigned integer that defines the height, in logical units, of the
* source and destination rectangles.
*/
int height;
private int height;
/**
* A 16-bit unsigned integer that defines the width, in logical units, of the
* source and destination rectangles.
*/
int width;
private int width;
/**
* A 16-bit unsigned integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the destination rectangle.
*/
int yDest;
private int yDest;
/**
* A 16-bit unsigned integer that defines the x-coordinate, in logical units, of the
* upper-left corner of the destination rectangle.
*/
int xDest;
private int xDest;
/**
* A variable-sized DeviceIndependentBitmap Object that is the source of the color data.
*/
HwmfBitmapDib dib;
private HwmfBitmapDib dib;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setDibToDev;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
colorUsage = leis.readUShort();
scanCount = leis.readUShort();
@ -615,14 +734,24 @@ public class HwmfFill {
int size = 9*LittleEndianConsts.SHORT_SIZE;
dib = new HwmfBitmapDib();
size += dib.init(leis);
size += dib.init(leis, (int)(recordSize-6-size));
return size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
@Override
public BufferedImage getImage() {
return dib.getImage();
}
}
public static class WmfDibBitBlt implements HwmfRecord {
public static class WmfDibBitBlt implements HwmfRecord, HwmfImageRecord {
/**
* A 32-bit unsigned integer that defines how the source pixels, the current brush
@ -633,51 +762,53 @@ public class HwmfFill {
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the source rectangle.
*/
int ySrc;
private int ySrc;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the source rectangle.
*/
int xSrc;
private int xSrc;
/**
* A 16-bit signed integer that defines the height, in logical units, of the source and
* destination rectangles.
*/
int height;
private int height;
/**
* A 16-bit signed integer that defines the width, in logical units, of the source and destination
* rectangles.
*/
int width;
private int width;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the upper-left
* corner of the destination rectangle.
*/
int yDest;
private int yDest;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the upper-left
* corner of the destination rectangle.
*/
int xDest;
private int xDest;
/**
* A variable-sized DeviceIndependentBitmap Object that defines image content.
* This object MUST be specified, even if the raster operation does not require a source.
*/
HwmfBitmapDib target;
private HwmfBitmapDib target;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.dibBitBlt;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3));
int size = 0;
int rasterOpIndex = leis.readUShort();
int rasterOpCode = leis.readUShort();
int rasterOpIndex = leis.readUShort();
rasterOperation = HwmfTernaryRasterOp.fromOpIndex(rasterOpIndex);
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
assert(rasterOpCode == rasterOperation.opCode);
ySrc = leis.readShort();
@ -696,76 +827,88 @@ public class HwmfFill {
size += 4*LittleEndianConsts.SHORT_SIZE;
if (hasBitmap) {
target = new HwmfBitmapDib();
size += target.init(leis);
size += target.init(leis, (int)(recordSize-6-size));
}
return size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
@Override
public BufferedImage getImage() {
return target.getImage();
}
}
public static class WmfDibStretchBlt implements HwmfRecord {
public static class WmfDibStretchBlt implements HwmfRecord, HwmfImageRecord {
/**
* A 32-bit unsigned integer that defines how the source pixels, the current brush
* in the playback device context, and the destination pixels are to be combined to form the
* new image. This code MUST be one of the values in the Ternary Raster Operation Enumeration.
*/
HwmfTernaryRasterOp rasterOperation;
private HwmfTernaryRasterOp rasterOperation;
/**
* A 16-bit signed integer that defines the height, in logical units, of the source rectangle.
*/
int srcHeight;
private int srcHeight;
/**
* A 16-bit signed integer that defines the width, in logical units, of the source rectangle.
*/
int srcWidth;
private int srcWidth;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the source rectangle.
*/
int ySrc;
private int ySrc;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* upper-left corner of the source rectangle.
*/
int xSrc;
private int xSrc;
/**
* A 16-bit signed integer that defines the height, in logical units, of the
* destination rectangle.
*/
int destHeight;
private int destHeight;
/**
* A 16-bit signed integer that defines the width, in logical units, of the
* destination rectangle.
*/
int destWidth;
private int destWidth;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units,
* of the upper-left corner of the destination rectangle.
*/
int yDest;
private int yDest;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units,
* of the upper-left corner of the destination rectangle.
*/
int xDest;
private int xDest;
/**
* A variable-sized DeviceIndependentBitmap Object that defines image content.
* This object MUST be specified, even if the raster operation does not require a source.
*/
HwmfBitmapDib target;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.dibStretchBlt;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3));
int size = 0;
int rasterOpIndex = leis.readUShort();
int rasterOpCode = leis.readUShort();
int rasterOpIndex = leis.readUShort();
rasterOperation = HwmfTernaryRasterOp.fromOpIndex(rasterOpIndex);
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
assert(rasterOpCode == rasterOperation.opCode);
srcHeight = leis.readShort();
@ -785,10 +928,20 @@ public class HwmfFill {
size += 4*LittleEndianConsts.SHORT_SIZE;
if (hasBitmap) {
target = new HwmfBitmapDib();
size += target.init(leis);
size += target.init(leis, (int)(recordSize-6-size));
}
return size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
@Override
public BufferedImage getImage() {
return target.getImage();
}
}
}

View File

@ -20,6 +20,7 @@ package org.apache.poi.hwmf.record;
import java.io.IOException;
import java.nio.charset.Charset;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
/**
@ -440,7 +441,7 @@ public class HwmfFont {
*
* @see WmfClipPrecision
*/
int clipPrecision;
WmfClipPrecision clipPrecision;
/**
* An 8-bit unsigned integer that defines the output quality.
@ -477,22 +478,23 @@ public class HwmfFont {
strikeOut = leis.readByte() != 0;
charSet = WmfCharset.valueOf(leis.readUByte());
outPrecision = WmfOutPrecision.valueOf(leis.readUByte());
clipPrecision = WmfClipPrecision.valueOf(leis.readUByte());
quality = WmfFontQuality.valueOf(leis.readUByte());
int pitchAndFamily = leis.readUByte();
family = WmfFontFamilyClass.valueOf(pitchAndFamily & 0xF);
pitch = WmfFontPitch.valueOf((pitchAndFamily >>> 6) & 3);
byte buf[] = new byte[32], readBytes;
for (readBytes = 0; readBytes < 32; readBytes++) {
if ((buf[readBytes] = leis.readByte()) == 0) {
break;
byte buf[] = new byte[32], b, readBytes = 0;
do {
if (readBytes == 32) {
throw new IOException("Font facename can't be determined.");
}
}
if (readBytes == 1 || readBytes == 32) {
throw new IOException("Font facename can't be determined.");
}
buf[readBytes++] = b = leis.readByte();
} while (b != 0 && b != -1 && readBytes <= 32);
facename = new String(buf, 0, readBytes-1, Charset.forName("ISO-8859-1"));
return 17+readBytes;
return 5*LittleEndianConsts.SHORT_SIZE+8*LittleEndianConsts.BYTE_SIZE+readBytes;
}
}

View File

@ -21,17 +21,17 @@ package org.apache.poi.hwmf.record;
* The HatchStyle Enumeration specifies the hatch pattern.
*/
public enum HwmfHatchStyle {
/** A horizontal hatch */
/** ----- - A horizontal hatch */
HS_HORIZONTAL(0x0000),
/** A vertical hatch */
/** ||||| - A vertical hatch */
HS_VERTICAL(0x0001),
/** A 45-degree downward, left-to-right hatch. */
/** \\\\\ - A 45-degree downward, left-to-right hatch. */
HS_FDIAGONAL(0x0002),
/** A 45-degree upward, left-to-right hatch. */
/** ///// - A 45-degree upward, left-to-right hatch. */
HS_BDIAGONAL(0x0003),
/** A horizontal and vertical cross-hatch. */
/** +++++ - A horizontal and vertical cross-hatch. */
HS_CROSS(0x0004),
/** A 45-degree crosshatch. */
/** xxxxx - A 45-degree crosshatch. */
HS_DIAGCROSS(0x0005);
int flag;

View File

@ -0,0 +1,114 @@
/* ====================================================================
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.hwmf.record;
/**
* A 16-bit unsigned integer that defines the mapping mode.
*
* The MapMode defines how logical units are mapped to physical units;
* that is, assuming that the origins in both the logical and physical coordinate systems
* are at the same point on the drawing surface, what is the physical coordinate (x',y')
* that corresponds to logical coordinate (x,y).
*
* For example, suppose the mapping mode is MM_TEXT. Given the following definition of that
* mapping mode, and an origin (0,0) at the top left corner of the drawing surface, logical
* coordinate (4,5) would map to physical coordinate (4,5) in pixels.
*
* Now suppose the mapping mode is MM_LOENGLISH, with the same origin as the previous
* example. Given the following definition of that mapping mode, logical coordinate (4,-5)
* would map to physical coordinate (0.04,0.05) in inches.
*/
public enum HwmfMapMode {
/**
* Each logical unit is mapped to one device pixel.
* Positive x is to the right; positive y is down.
*/
MM_TEXT(0x0001, 0),
/**
* Each logical unit is mapped to 0.1 millimeter.
* Positive x is to the right; positive y is up.
*/
MM_LOMETRIC(0x0002, 254),
/**
* Each logical unit is mapped to 0.01 millimeter.
* Positive x is to the right; positive y is up.
*/
MM_HIMETRIC(0x0003, 2540),
/**
* Each logical unit is mapped to 0.01 inch.
* Positive x is to the right; positive y is up.
*/
MM_LOENGLISH(0x0004, 100),
/**
* Each logical unit is mapped to 0.001 inch.
* Positive x is to the right; positive y is up.
*/
MM_HIENGLISH(0x0005, 1000),
/**
* Each logical unit is mapped to one twentieth (1/20) of a point.
* In printing, a point is 1/72 of an inch; therefore, 1/20 of a point is 1/1440 of an inch.
* This unit is also known as a "twip".
* Positive x is to the right; positive y is up.
*/
MM_TWIPS(0x0006, 1440),
/**
* Logical units are mapped to arbitrary device units with equally scaled axes;
* that is, one unit along the x-axis is equal to one unit along the y-axis.
* The META_SETWINDOWEXT and META_SETVIEWPORTEXT records specify the units and the
* orientation of the axes.
* The processing application SHOULD make adjustments as necessary to ensure the x and y
* units remain the same size. For example, when the window extent is set, the viewport
* SHOULD be adjusted to keep the units isotropic.
*/
MM_ISOTROPIC(0x0007, -1),
/**
* Logical units are mapped to arbitrary units with arbitrarily scaled axes.
*/
MM_ANISOTROPIC(0x0008, -1);
/**
* native flag
*/
public final int flag;
/**
* transformation units - usually scale relative to current dpi.
* when scale == 0, then don't scale
* when scale == -1, then scale relative to window dimension.
*/
public final int scale;
HwmfMapMode(int flag, int scale) {
this.flag = flag;
this.scale = scale;
}
static HwmfMapMode valueOf(int flag) {
for (HwmfMapMode mm : values()) {
if (mm.flag == flag) return mm;
}
return null;
}
}

View File

@ -17,8 +17,12 @@
package org.apache.poi.hwmf.record;
import java.awt.image.BufferedImage;
import java.io.IOException;
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@ -28,22 +32,38 @@ public class HwmfMisc {
* The META_SAVEDC record saves the playback device context for later retrieval.
*/
public static class WmfSaveDc implements HwmfRecord {
public HwmfRecordType getRecordType() { return HwmfRecordType.saveDc; }
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.saveDc;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
return 0;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
* The META_SETRELABS record is reserved and not supported.
*/
public static class WmfSetRelabs implements HwmfRecord {
public HwmfRecordType getRecordType() { return HwmfRecordType.setRelabs; }
public HwmfRecordType getRecordType() {
return HwmfRecordType.setRelabs;
}
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
return 0;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -57,16 +77,23 @@ public class HwmfMisc {
* member is positive, nSavedDC represents a specific instance of the state to be restored. If
* this member is negative, nSavedDC represents an instance relative to the current state.
*/
int nSavedDC;
private int nSavedDC;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.restoreDc;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
nSavedDC = leis.readShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -75,16 +102,23 @@ public class HwmfMisc {
*/
public static class WmfSetBkColor implements HwmfRecord {
HwmfColorRef colorRef;
private HwmfColorRef colorRef;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setBkColor;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
colorRef = new HwmfColorRef();
return colorRef.init(leis);
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.getProperties().setBackgroundColor(colorRef);
}
}
/**
@ -96,18 +130,38 @@ public class HwmfMisc {
/**
* A 16-bit unsigned integer that defines background mix mode.
* This MUST be either TRANSPARENT = 0x0001 or OPAQUE = 0x0002
*/
int bkMode;
public enum HwmfBkMode {
TRANSPARENT(0x0001), OPAQUE(0x0002);
int flag;
HwmfBkMode(int flag) {
this.flag = flag;
}
static HwmfBkMode valueOf(int flag) {
for (HwmfBkMode bs : values()) {
if (bs.flag == flag) return bs;
}
return null;
}
}
private HwmfBkMode bkMode;
public HwmfRecordType getRecordType() {
return HwmfRecordType.setBkMode;
}
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
bkMode = leis.readUShort();
bkMode = HwmfBkMode.valueOf(leis.readUShort());
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.getProperties().setBkMode(bkMode);
}
}
/**
@ -122,19 +176,26 @@ public class HwmfMisc {
* LAYOUT_RTL = 0x0001
* LAYOUT_BITMAPORIENTATIONPRESERVED = 0x0008
*/
int layout;
private int layout;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setLayout;
}
@SuppressWarnings("unused")
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
layout = leis.readUShort();
// A 16-bit field that MUST be ignored.
int reserved = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -144,72 +205,23 @@ public class HwmfMisc {
*/
public static class WmfSetMapMode implements HwmfRecord {
/**
* A 16-bit unsigned integer that defines the mapping mode.
*
* The MapMode defines how logical units are mapped to physical units;
* that is, assuming that the origins in both the logical and physical coordinate systems
* are at the same point on the drawing surface, what is the physical coordinate (x',y')
* that corresponds to logical coordinate (x,y).
*
* For example, suppose the mapping mode is MM_TEXT. Given the following definition of that
* mapping mode, and an origin (0,0) at the top left corner of the drawing surface, logical
* coordinate (4,5) would map to physical coordinate (4,5) in pixels.
*
* Now suppose the mapping mode is MM_LOENGLISH, with the same origin as the previous
* example. Given the following definition of that mapping mode, logical coordinate (4,-5)
* would map to physical coordinate (0.04,0.05) in inches.
*
* This MUST be one of the following:
*
* MM_TEXT (= 0x0001):
* Each logical unit is mapped to one device pixel.
* Positive x is to the right; positive y is down.
*
* MM_LOMETRIC (= 0x0002):
* Each logical unit is mapped to 0.1 millimeter.
* Positive x is to the right; positive y is up.
*
* MM_HIMETRIC (= 0x0003):
* Each logical unit is mapped to 0.01 millimeter.
* Positive x is to the right; positive y is up.
*
* MM_LOENGLISH (= 0x0004):
* Each logical unit is mapped to 0.01 inch.
* Positive x is to the right; positive y is up.
*
* MM_HIENGLISH (= 0x0005):
* Each logical unit is mapped to 0.001 inch.
* Positive x is to the right; positive y is up.
*
* MM_TWIPS (= 0x0006):
* Each logical unit is mapped to one twentieth (1/20) of a point.
* In printing, a point is 1/72 of an inch; therefore, 1/20 of a point is 1/1440 of an inch.
* This unit is also known as a "twip".
* Positive x is to the right; positive y is up.
*
* MM_ISOTROPIC (= 0x0007):
* Logical units are mapped to arbitrary device units with equally scaled axes;
* that is, one unit along the x-axis is equal to one unit along the y-axis.
* The META_SETWINDOWEXT and META_SETVIEWPORTEXT records specify the units and the
* orientation of the axes.
* The processing application SHOULD make adjustments as necessary to ensure the x and y
* units remain the same size. For example, when the window extent is set, the viewport
* SHOULD be adjusted to keep the units isotropic.
*
* MM_ANISOTROPIC (= 0x0008):
* Logical units are mapped to arbitrary units with arbitrarily scaled axes.
*/
int mapMode;
private HwmfMapMode mapMode;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setMapMode;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
mapMode = leis.readUShort();
mapMode = HwmfMapMode.valueOf(leis.readUShort());
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.getProperties().setMapMode(mapMode);
}
}
/**
@ -223,16 +235,23 @@ public class HwmfMisc {
* match a font's aspect ratio to the current device's aspect ratio. If bit 0 is
* set, the mapper selects only matching fonts.
*/
long mapperValues;
private long mapperValues;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setMapperFlags;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
mapperValues = leis.readUInt();
return LittleEndianConsts.INT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -262,16 +281,23 @@ public class HwmfMisc {
* R2_MERGEPEN = 0x000F,
* R2_WHITE = 0x0010
*/
int drawMode;
private int drawMode;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setRop2;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
drawMode = leis.readUShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -288,25 +314,32 @@ public class HwmfMisc {
* COLORONCOLOR = 0x0003,
* HALFTONE = 0x0004
*/
int setStretchBltMode;
private int setStretchBltMode;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setStretchBltMode;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
setStretchBltMode = leis.readUShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
* The META_DIBCREATEPATTERNBRUSH record creates a Brush Object with a
* pattern specified by a DeviceIndependentBitmap (DIB) Object
*/
public static class WmfDibCreatePatternBrush implements HwmfRecord {
public static class WmfDibCreatePatternBrush implements HwmfRecord, HwmfImageRecord {
HwmfBrushStyle style;
private HwmfBrushStyle style;
/**
* A 16-bit unsigned integer that defines whether the Colors field of a DIB
@ -320,15 +353,17 @@ public class HwmfMisc {
* DIB_PAL_COLORS = 0x0001,
* DIB_PAL_INDICES = 0x0002
*/
int colorUsage;
private int colorUsage;
HwmfBitmapDib patternDib;
HwmfBitmap16 pattern16;
private HwmfBitmapDib patternDib;
private HwmfBitmap16 pattern16;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.dibCreatePatternBrush;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
style = HwmfBrushStyle.valueOf(leis.readUShort());
colorUsage = leis.readUShort();
@ -339,12 +374,9 @@ public class HwmfMisc {
case BS_DIBPATTERN:
case BS_DIBPATTERNPT:
case BS_HATCHED:
patternDib = new HwmfBitmapDib();
size += patternDib.init(leis);
break;
case BS_PATTERN:
pattern16 = new HwmfBitmap16();
size += pattern16.init(leis);
patternDib = new HwmfBitmapDib();
size += patternDib.init(leis, (int)(recordSize-6-size));
break;
case BS_INDEXED:
case BS_DIBPATTERN8X8:
@ -354,6 +386,24 @@ public class HwmfMisc {
}
return size;
}
@Override
public void draw(HwmfGraphics ctx) {
HwmfDrawProperties prop = ctx.getProperties();
prop.setBrushStyle(style);
prop.setBrushBitmap(getImage());
}
@Override
public BufferedImage getImage() {
if (patternDib != null) {
return patternDib.getImage();
} else if (pattern16 != null) {
return pattern16.getImage();
} else {
return null;
}
}
}
/**
@ -364,84 +414,94 @@ public class HwmfMisc {
public static class WmfDeleteObject implements HwmfRecord {
/**
* A 16-bit unsigned integer used to index into the WMF Object Table to
get the object to be deleted.
* get the object to be deleted.
*/
int objectIndex;
private int objectIndex;
public HwmfRecordType getRecordType() { return HwmfRecordType.deleteObject; }
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.deleteObject;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
objectIndex = leis.readUShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
public static class WmfCreatePatternBrush implements HwmfRecord {
HwmfBitmap16 pattern;
private HwmfBitmap16 pattern;
public HwmfRecordType getRecordType() { return HwmfRecordType.createPatternBrush; }
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.createPatternBrush;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
pattern = new HwmfBitmap16(true);
return pattern.init(leis);
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
public static class WmfCreatePenIndirect implements HwmfRecord {
/**
* A 16-bit unsigned integer that specifies the pen style.
* The value MUST be defined from the PenStyle Enumeration table.
*
* PS_COSMETIC = 0x0000,
* PS_ENDCAP_ROUND = 0x0000,
* PS_JOIN_ROUND = 0x0000,
* PS_SOLID = 0x0000,
* PS_DASH = 0x0001,
* PS_DOT = 0x0002,
* PS_DASHDOT = 0x0003,
* PS_DASHDOTDOT = 0x0004,
* PS_NULL = 0x0005,
* PS_INSIDEFRAME = 0x0006,
* PS_USERSTYLE = 0x0007,
* PS_ALTERNATE = 0x0008,
* PS_ENDCAP_SQUARE = 0x0100,
* PS_ENDCAP_FLAT = 0x0200,
* PS_JOIN_BEVEL = 0x1000,
* PS_JOIN_MITER = 0x2000
*/
int penStyle;
private HwmfPenStyle penStyle;
/**
* A 32-bit PointS Object that specifies a point for the object dimensions.
* The xcoordinate is the pen width. The y-coordinate is ignored.
*/
int xWidth, yWidth;
private int xWidth;
@SuppressWarnings("unused")
private int yWidth;
/**
* A 32-bit ColorRef Object that specifies the pen color value.
*/
HwmfColorRef colorRef;
private HwmfColorRef colorRef;
public HwmfRecordType getRecordType() { return HwmfRecordType.createPenIndirect; }
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.createPenIndirect;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
penStyle = leis.readUShort();
penStyle = HwmfPenStyle.valueOf(leis.readUShort());
xWidth = leis.readShort();
yWidth = leis.readShort();
colorRef = new HwmfColorRef();
int size = 3*LittleEndianConsts.SHORT_SIZE;
size += colorRef.init(leis);
return size;
int size = colorRef.init(leis);
return size+3*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
HwmfDrawProperties p = ctx.getProperties();
p.setPenStyle(penStyle);
p.setPenColor(colorRef);
p.setPenWidth(xWidth);
}
}
/**
* The META_CREATEBRUSHINDIRECT record creates a Brush Object
* from a LogBrush Object.
*
*
* The following table shows the relationship between values in the BrushStyle,
* ColorRef and BrushHatch fields in a LogBrush Object. Only supported brush styles are listed.
*
*
* <table>
* <tr>
* <th>BrushStyle</th>
@ -481,26 +541,37 @@ public class HwmfMisc {
* </table>
*/
public static class WmfCreateBrushIndirect implements HwmfRecord {
HwmfBrushStyle brushStyle;
private HwmfBrushStyle brushStyle;
HwmfColorRef colorRef;
private HwmfColorRef colorRef;
/**
* A 16-bit field that specifies the brush hatch type.
* Its interpretation depends on the value of BrushStyle.
*
*
*/
HwmfHatchStyle brushHatch;
private HwmfHatchStyle brushHatch;
public HwmfRecordType getRecordType() { return HwmfRecordType.createBrushIndirect; }
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.createBrushIndirect;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
brushStyle = HwmfBrushStyle.valueOf(leis.readUShort());
colorRef = new HwmfColorRef();
int size = colorRef.init(leis);
brushHatch = HwmfHatchStyle.valueOf(leis.readUShort());
size += 4;
return size;
return size+2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
HwmfDrawProperties p = ctx.getProperties();
p.setBrushStyle(brushStyle);
p.setBrushColor(colorRef);
p.setBrushHatch(brushHatch);
}
}
}

View File

@ -19,6 +19,7 @@ package org.apache.poi.hwmf.record;
import java.io.IOException;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@ -30,7 +31,7 @@ public class HwmfPalette {
// Blue (1 byte): An 8-bit unsigned integer that defines the blue intensity value for the palette entry.
// Green (1 byte): An 8-bit unsigned integer that defines the green intensity value for the palette entry.
// Red (1 byte): An 8-bit unsigned integer that defines the red intensity value for the palette entry.
int values, blue, green, red;
private int values, blue, green, red;
public int init(LittleEndianInputStream leis) throws IOException {
values = leis.readUByte();
@ -48,16 +49,17 @@ public class HwmfPalette {
* used with the META_SETPALENTRIES and META_ANIMATEPALETTE record types.
* When used with META_CREATEPALETTE, it MUST be 0x0300
*/
int start;
private int start;
/**
* NumberOfEntries (2 bytes): A 16-bit unsigned integer that defines the number of objects in
* aPaletteEntries.
*/
int numberOfEntries;
private int numberOfEntries;
PaletteEntry entries[];
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
start = leis.readUShort();
numberOfEntries = leis.readUShort();
@ -69,15 +71,26 @@ public class HwmfPalette {
}
return size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
* The META_CREATEPALETTE record creates a Palette Object
*/
public static class WmfCreatePalette extends WmfPaletteParent {
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.createPalette;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -85,9 +98,15 @@ public class HwmfPalette {
* palette that is defined in the playback device context.
*/
public static class WmfSetPaletteEntries extends WmfPaletteParent {
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setPalEntries;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -101,14 +120,21 @@ public class HwmfPalette {
*/
int numberOfEntries;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.resizePalette;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
numberOfEntries = leis.readUShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -119,16 +145,23 @@ public class HwmfPalette {
* A 16-bit unsigned integer used to index into the WMF Object Table to get
* the Palette Object to be selected.
*/
int palette;
private int palette;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.selectPalette;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
palette = leis.readUShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -136,11 +169,20 @@ public class HwmfPalette {
* is defined in the playback device context to the system palette.
*/
public static class WmfRealizePalette implements HwmfRecord {
public HwmfRecordType getRecordType() { return HwmfRecordType.realizePalette; }
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.realizePalette;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
return 0;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -156,8 +198,14 @@ public class HwmfPalette {
* this record SHOULD have no effect.
*/
public static class WmfAnimatePalette extends WmfPaletteParent {
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.animatePalette;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
}

View File

@ -0,0 +1,171 @@
/* ====================================================================
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.hwmf.record;
import java.awt.BasicStroke;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
/**
* The 16-bit PenStyle Enumeration is used to specify different types of pens
* that can be used in graphics operations.
*
* Various styles can be combined by using a logical OR statement, one from
* each subsection of Style, EndCap, Join, and Type (Cosmetic).
*
* The defaults in case the other values of the subsection aren't set are
* solid, round end caps, round joins and cosmetic type.
*/
public class HwmfPenStyle {
public enum HwmfLineCap {
/** Rounded ends */
ROUND(0, BasicStroke.CAP_ROUND),
/** Square protrudes by half line width */
SQUARE(1, BasicStroke.CAP_SQUARE),
/** Line ends at end point*/
FLAT(2, BasicStroke.CAP_BUTT);
public int wmfFlag;
public int awtFlag;
HwmfLineCap(int wmfFlag, int awtFlag) {
this.wmfFlag = wmfFlag;
this.awtFlag = awtFlag;
}
static HwmfLineCap valueOf(int wmfFlag) {
for (HwmfLineCap hs : values()) {
if (hs.wmfFlag == wmfFlag) return hs;
}
return null;
}
}
public enum HwmfLineJoin {
/**Line joins are round. */
ROUND(0, BasicStroke.JOIN_ROUND),
/** Line joins are beveled. */
BEVEL(1, BasicStroke.JOIN_BEVEL),
/**
* Line joins are mitered when they are within the current limit set by the
* SETMITERLIMIT META_ESCAPE record. A join is beveled when it would exceed the limit
*/
MITER(2, BasicStroke.JOIN_MITER);
public int wmfFlag;
public int awtFlag;
HwmfLineJoin(int wmfFlag, int awtFlag) {
this.wmfFlag = wmfFlag;
this.awtFlag = awtFlag;
}
static HwmfLineJoin valueOf(int wmfFlag) {
for (HwmfLineJoin hs : values()) {
if (hs.wmfFlag == wmfFlag) return hs;
}
return null;
}
}
public enum HwmfLineDash {
/**
* The pen is solid.
*/
SOLID(0x0000, 10),
/**
* The pen is dashed. (-----)
*/
DASH(0x0001, 10, 8),
/**
* The pen is dotted. (.....)
*/
DOT(0x0002, 2, 4),
/**
* The pen has alternating dashes and dots. (_._._._)
*/
DASHDOT(0x0003, 10, 8, 2, 8),
/**
* The pen has dashes and double dots. (_.._.._)
*/
DASHDOTDOT(0x0004, 10, 4, 2, 4, 2, 4),
/**
* The pen is invisible.
*/
NULL(0x0005),
/**
* The pen is solid. When this pen is used in any drawing record that takes a
* bounding rectangle, the dimensions of the figure are shrunk so that it fits
* entirely in the bounding rectangle, taking into account the width of the pen.
*/
INSIDEFRAME(0x0006, 10),
/**
* The pen uses a styling array supplied by the user.
* (this is currently not supported and drawn as solid ... no idea where the user
* styling is supposed to come from ...)
*/
USERSTYLE(0x0007, 10);
public int wmfFlag;
public float[] dashes;
HwmfLineDash(int wmfFlag, float... dashes) {
this.wmfFlag = wmfFlag;
this.dashes = dashes;
}
static HwmfLineDash valueOf(int wmfFlag) {
for (HwmfLineDash hs : values()) {
if (hs.wmfFlag == wmfFlag) return hs;
}
return null;
}
}
private static final BitField SUBSECTION_DASH = BitFieldFactory.getInstance(0x0007);
private static final BitField SUBSECTION_ALTERNATE = BitFieldFactory.getInstance(0x0008);
private static final BitField SUBSECTION_ENDCAP = BitFieldFactory.getInstance(0x0300);
private static final BitField SUBSECTION_JOIN = BitFieldFactory.getInstance(0x3000);
private int flag;
public static HwmfPenStyle valueOf(int flag) {
HwmfPenStyle ps = new HwmfPenStyle();
ps.flag = flag;
return ps;
}
public HwmfLineCap getLineCap() {
return HwmfLineCap.valueOf(SUBSECTION_ENDCAP.getValue(flag));
}
public HwmfLineJoin getLineJoin() {
return HwmfLineJoin.valueOf(SUBSECTION_JOIN.getValue(flag));
}
public HwmfLineDash getLineDash() {
return HwmfLineDash.valueOf(SUBSECTION_DASH.getValue(flag));
}
/**
* The pen sets every other pixel (this style is applicable only for cosmetic pens).
*/
public boolean isAlternateDash() {
return SUBSECTION_ALTERNATE.isSet(flag);
}
}

View File

@ -19,6 +19,7 @@ package org.apache.poi.hwmf.record;
import java.io.IOException;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.util.LittleEndianInputStream;
public interface HwmfRecord {
@ -32,4 +33,11 @@ public interface HwmfRecord {
* @throws IOException
*/
int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException;
/**
* Apply the record settings to the graphics context
*
* @param ctx the graphics context to modify
*/
void draw(HwmfGraphics ctx);
}

View File

@ -285,7 +285,7 @@ public enum HwmfTernaryRasterOp {
this.opCmd=opCmd;
}
public static HwmfTernaryRasterOp fromOpIndex(int opIndex) {
public static HwmfTernaryRasterOp valueOf(int opIndex) {
for (HwmfTernaryRasterOp bb : HwmfTernaryRasterOp.values()) {
if (bb.opIndex == opIndex) {
return bb;

View File

@ -19,9 +19,12 @@ package org.apache.poi.hwmf.record;
import java.io.IOException;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
import org.apache.poi.util.LocaleUtil;
import org.apache.poi.util.RecordFormatException;
public class HwmfText {
@ -38,16 +41,23 @@ public class HwmfText {
* this value is transformed and rounded to the nearest pixel. For details about setting the
* mapping mode, see META_SETMAPMODE
*/
int charExtra;
private int charExtra;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setTextCharExtra;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
charExtra = leis.readUShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -55,16 +65,23 @@ public class HwmfText {
*/
public static class WmfSetTextColor implements HwmfRecord {
HwmfColorRef colorRef;
private HwmfColorRef colorRef;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setTextColor;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
colorRef = new HwmfColorRef();
return colorRef.init(leis);
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -76,7 +93,7 @@ public class HwmfText {
/**
* A 16-bit unsigned integer that specifies the number of space characters in the line.
*/
int breakCount;
private int breakCount;
/**
* A 16-bit unsigned integer that specifies the total extra space, in logical
@ -84,17 +101,24 @@ public class HwmfText {
* identified by the BreakExtra member is transformed and rounded to the nearest pixel. For
* details about setting the mapping mode, see {@link WmfSetMapMode}.
*/
int breakExtra;
private int breakExtra;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setBkColor;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
breakCount = leis.readUShort();
breakExtra = leis.readUShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -105,7 +129,7 @@ public class HwmfText {
/**
* A 16-bit signed integer that defines the length of the string, in bytes, pointed to by String.
*/
int stringLength;
private int stringLength;
/**
* The size of this field MUST be a multiple of two. If StringLength is an odd
* number, then this field MUST be of a size greater than or equal to StringLength + 1.
@ -114,31 +138,38 @@ public class HwmfText {
* length of the string.
* The string is written at the location specified by the XStart and YStart fields.
*/
String text;
private String text;
/**
* A 16-bit signed integer that defines the vertical (y-axis) coordinate, in logical
* units, of the point where drawing is to start.
*/
int yStart;
private int yStart;
/**
* A 16-bit signed integer that defines the horizontal (x-axis) coordinate, in
* logical units, of the point where drawing is to start.
*/
int xStart;
private int xStart;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.textOut;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
stringLength = leis.readShort();
byte buf[] = new byte[stringLength+(stringLength%2)];
byte buf[] = new byte[stringLength+(stringLength&1)];
leis.readFully(buf);
text = new String(buf, "UTF16-LE").trim();
text = new String(buf, 0, stringLength, LocaleUtil.CHARSET_1252).trim();
yStart = leis.readShort();
xStart = leis.readShort();
return 3*LittleEndianConsts.SHORT_SIZE+buf.length;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -152,16 +183,16 @@ public class HwmfText {
* A 16-bit signed integer that defines the y-coordinate, in logical units, where the
text string is to be located.
*/
int y;
private int y;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, where the
text string is to be located.
*/
int x;
private int x;
/**
* A 16-bit signed integer that defines the length of the string.
*/
int stringLength;
private int stringLength;
/**
* A 16-bit unsigned integer that defines the use of the application-defined
* rectangle. This member can be a combination of one or more values in the
@ -196,7 +227,7 @@ public class HwmfText {
* Indicates that both horizontal and vertical character displacement values
* SHOULD be provided.
*/
int fwOpts;
private int fwOpts;
/**
* An optional 8-byte Rect Object (section 2.2.2.18) that defines the
* dimensions, in logical coordinates, of a rectangle that is used for clipping, opaquing, or both.
@ -205,43 +236,58 @@ public class HwmfText {
* Each value is a 16-bit signed integer that defines the coordinate, in logical coordinates, of
* the upper-left corner of the rectangle
*/
int left,top,right,bottom;
private int left,top,right,bottom;
/**
* A variable-length string that specifies the text to be drawn. The string does
* not need to be null-terminated, because StringLength specifies the length of the string. If
* the length is odd, an extra byte is placed after it so that the following member (optional Dx) is
* aligned on a 16-bit boundary.
*/
String text;
private String text;
/**
* An optional array of 16-bit signed integers that indicate the distance between
* origins of adjacent character cells. For example, Dx[i] logical units separate the origins of
* character cell i and character cell i + 1. If this field is present, there MUST be the same
* number of values as there are characters in the string.
*/
int dx[];
private int dx[];
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.extTextOut;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
y = leis.readShort();
x = leis.readShort();
stringLength = leis.readShort();
fwOpts = leis.readUShort();
left = leis.readShort();
top = leis.readShort();
right = leis.readShort();
bottom = leis.readShort();
byte buf[] = new byte[stringLength+(stringLength%2)];
int size = 4*LittleEndianConsts.SHORT_SIZE;
if (fwOpts != 0) {
// the bounding rectangle is optional and only read when fwOpts are given
left = leis.readShort();
top = leis.readShort();
right = leis.readShort();
bottom = leis.readShort();
size += 4*LittleEndianConsts.SHORT_SIZE;
}
byte buf[] = new byte[stringLength+(stringLength&1)];
leis.readFully(buf);
text = new String(buf, "UTF16-LE");
text = new String(buf, 0, stringLength, LocaleUtil.CHARSET_1252);
size += buf.length;
int size = 8*LittleEndianConsts.SHORT_SIZE+buf.length;
if (size < recordSize) {
dx = new int[text.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);
}
dx = new int[stringLength];
for (int i=0; i<dx.length; i++) {
dx[i] = leis.readShort();
}
@ -250,6 +296,11 @@ public class HwmfText {
return size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
@ -320,28 +371,42 @@ public class HwmfText {
* VTA_BASELINE (0x0018):
* The reference point MUST be on the baseline of the text.
*/
int textAlignmentMode;
private int textAlignmentMode;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setTextAlign;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
textAlignmentMode = leis.readUShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
public static class WmfCreateFontIndirect implements HwmfRecord {
HwmfFont font;
private HwmfFont font;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.createFontIndirect;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
font = new HwmfFont();
return font.init(leis);
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
}

View File

@ -17,43 +17,17 @@
package org.apache.poi.hwmf.record;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
public class HwmfWindowing {
/**
* The META_OFFSETCLIPRGN record moves the clipping region in the playback device context by the
* specified offsets.
*/
public static class WmfOffsetClipRgn implements HwmfRecord {
/**
* A 16-bit signed integer that defines the number of logical units to move up or down.
*/
int yOffset;
/**
* A 16-bit signed integer that defines the number of logical units to move left or right.
*/
int xOffset;
public HwmfRecordType getRecordType() {
return HwmfRecordType.offsetClipRgn;
}
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
yOffset = leis.readShort();
xOffset = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
}
/**
* The META_SETVIEWPORTORG record defines the viewport origin in the playback device context.
*/
@ -62,22 +36,29 @@ public class HwmfWindowing {
/**
* A 16-bit signed integer that defines the vertical offset, in device units.
*/
int y;
private int y;
/**
* A 16-bit signed integer that defines the horizontal offset, in device units.
*/
int x;
private int x;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setViewportOrg;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
y = leis.readShort();
x = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.getProperties().setViewportOrg(x, y);
}
}
/**
@ -90,23 +71,30 @@ public class HwmfWindowing {
* A 16-bit signed integer that defines the vertical extent
* of the viewport in device units.
*/
int y;
private int height;
/**
* A 16-bit signed integer that defines the horizontal extent
* of the viewport in device units.
*/
int x;
private int width;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setViewportExt;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
y = leis.readShort();
x = leis.readShort();
height = leis.readShort();
width = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.getProperties().setViewportExt(width, height);
}
}
/**
@ -118,22 +106,30 @@ public class HwmfWindowing {
/**
* A 16-bit signed integer that defines the vertical offset, in device units.
*/
int yOffset;
private int yOffset;
/**
* A 16-bit signed integer that defines the horizontal offset, in device units.
*/
int xOffset;
private int xOffset;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.offsetViewportOrg;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
yOffset = leis.readShort();
xOffset = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
Rectangle2D viewport = ctx.getProperties().getViewport();
ctx.getProperties().setViewportOrg(viewport.getX()+xOffset, viewport.getY()+yOffset);
}
}
/**
@ -144,22 +140,29 @@ public class HwmfWindowing {
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units.
*/
int y;
private int y;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units.
*/
int x;
private int x;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setWindowOrg;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
y = leis.readShort();
x = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.getProperties().setWindowOrg(x, y);
}
}
/**
@ -172,23 +175,30 @@ public class HwmfWindowing {
* A 16-bit signed integer that defines the vertical extent of
* the window in logical units.
*/
int y;
private int height;
/**
* A 16-bit signed integer that defines the horizontal extent of
* the window in logical units.
*/
int x;
private int width;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.setWindowExt;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
y = leis.readShort();
x = leis.readShort();
height = leis.readShort();
width = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
ctx.getProperties().setWindowExt(width, height);
}
}
/**
@ -200,22 +210,30 @@ public class HwmfWindowing {
/**
* A 16-bit signed integer that defines the vertical offset, in device units.
*/
int yOffset;
private int yOffset;
/**
* A 16-bit signed integer that defines the horizontal offset, in device units.
*/
int xOffset;
private int xOffset;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.offsetWindowOrg;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
yOffset = leis.readShort();
xOffset = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
Rectangle2D window = ctx.getProperties().getWindow();
ctx.getProperties().setWindowOrg(window.getX()+xOffset, window.getY()+yOffset);
}
}
/**
@ -228,30 +246,32 @@ public class HwmfWindowing {
* A 16-bit signed integer that defines the amount by which to divide the
* result of multiplying the current y-extent by the value of the yNum member.
*/
int yDenom;
private int yDenom;
/**
* A 16-bit signed integer that defines the amount by which to multiply the
* current y-extent.
*/
int yNum;
private int yNum;
/**
* A 16-bit signed integer that defines the amount by which to divide the
* result of multiplying the current x-extent by the value of the xNum member.
*/
int xDenom;
private int xDenom;
/**
* A 16-bit signed integer that defines the amount by which to multiply the
* current x-extent.
*/
int xNum;
private int xNum;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.scaleWindowExt;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
yDenom = leis.readShort();
yNum = leis.readShort();
@ -259,6 +279,14 @@ public class HwmfWindowing {
xNum = leis.readShort();
return 4*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
Rectangle2D window = ctx.getProperties().getWindow();
double width = window.getWidth() * xNum / xDenom;
double height = window.getHeight() * yNum / yDenom;
ctx.getProperties().setWindowExt(width, height);
}
}
@ -273,30 +301,32 @@ public class HwmfWindowing {
* A 16-bit signed integer that defines the amount by which to divide the
* result of multiplying the current y-extent by the value of the yNum member.
*/
int yDenom;
private int yDenom;
/**
* A 16-bit signed integer that defines the amount by which to multiply the
* current y-extent.
*/
int yNum;
private int yNum;
/**
* A 16-bit signed integer that defines the amount by which to divide the
* result of multiplying the current x-extent by the value of the xNum member.
*/
int xDenom;
private int xDenom;
/**
* A 16-bit signed integer that defines the amount by which to multiply the
* current x-extent.
*/
int xNum;
private int xNum;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.scaleViewportExt;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
yDenom = leis.readShort();
yNum = leis.readShort();
@ -304,6 +334,48 @@ public class HwmfWindowing {
xNum = leis.readShort();
return 4*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
Rectangle2D viewport = ctx.getProperties().getViewport();
double width = viewport.getWidth() * xNum / xDenom;
double height = viewport.getHeight() * yNum / yDenom;
ctx.getProperties().setViewportExt(width, height);
}
}
/**
* The META_OFFSETCLIPRGN record moves the clipping region in the playback device context by the
* specified offsets.
*/
public static class WmfOffsetClipRgn implements HwmfRecord {
/**
* A 16-bit signed integer that defines the number of logical units to move up or down.
*/
private int yOffset;
/**
* A 16-bit signed integer that defines the number of logical units to move left or right.
*/
private int xOffset;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.offsetClipRgn;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
yOffset = leis.readShort();
xOffset = leis.readShort();
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
@ -316,30 +388,32 @@ public class HwmfWindowing {
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* lower-right corner of the rectangle.
*/
int bottom;
private int bottom;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* lower-right corner of the rectangle.
*/
int right;
private int right;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the rectangle.
*/
int top;
private int top;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* upper-left corner of the rectangle.
*/
int left;
private int left;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.excludeClipRect;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
bottom = leis.readShort();
right = leis.readShort();
@ -347,6 +421,11 @@ public class HwmfWindowing {
left = leis.readShort();
return 4*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
@ -360,30 +439,32 @@ public class HwmfWindowing {
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* lower-right corner of the rectangle.
*/
int bottom;
private int bottom;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* lower-right corner of the rectangle.
*/
int right;
private int right;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the rectangle.
*/
int top;
private int top;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* upper-left corner of the rectangle.
*/
int left;
private int left;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.intersectClipRect;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
bottom = leis.readShort();
right = leis.readShort();
@ -391,11 +472,15 @@ public class HwmfWindowing {
left = leis.readShort();
return 4*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
/**
* The META_INTERSECTCLIPRECT record sets the clipping region in the playback device context to the
* intersection of the existing clipping region and the specified rectangle.
* The META_SELECTCLIPREGION record specifies a Region Object to be the current clipping region.
*/
public static class WmfSelectClipRegion implements HwmfRecord {
@ -403,16 +488,23 @@ public class HwmfWindowing {
* A 16-bit unsigned integer used to index into the WMF Object Table to get
* the region to be clipped.
*/
int region;
private int region;
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.selectClipRegion;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
region = leis.readShort();
return LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
public static class WmfScanObject {
@ -421,30 +513,30 @@ public class HwmfWindowing {
* coordinates in the ScanLines array. This value MUST be a multiple of 2, since left and right
* endpoints are required to specify each scanline.
*/
int count;
private int count;
/**
* A 16-bit unsigned integer that defines the vertical (y-axis) coordinate, in logical units, of the top scanline.
*/
int top;
private int top;
/**
* A 16-bit unsigned integer that defines the vertical (y-axis) coordinate, in logical units, of the bottom scanline.
*/
int bottom;
private int bottom;
/**
* A 16-bit unsigned integer that defines the horizontal (x-axis) coordinate,
* in logical units, of the left endpoint of the scanline.
*/
int left_scanline[];
private int left_scanline[];
/**
* A 16-bit unsigned integer that defines the horizontal (x-axis) coordinate,
* in logical units, of the right endpoint of the scanline.
*/
int right_scanline[];
private int right_scanline[];
/**
* A 16-bit unsigned integer that MUST be the same as the value of the Count
* field; it is present to allow upward travel in the structure.
*/
int count2;
private int count2;
public int init(LittleEndianInputStream leis) {
count = leis.readUShort();
@ -465,62 +557,64 @@ public class HwmfWindowing {
/**
* A 16-bit signed integer. A value that MUST be ignored.
*/
int nextInChain;
private int nextInChain;
/**
* A 16-bit signed integer that specifies the region identifier. It MUST be 0x0006.
*/
int objectType;
private int objectType;
/**
* A 32-bit unsigned integer. A value that MUST be ignored.
*/
int objectCount;
private int objectCount;
/**
* A 16-bit signed integer that defines the size of the region in bytes plus the size of aScans in bytes.
*/
int regionSize;
private int regionSize;
/**
* A 16-bit signed integer that defines the number of scanlines composing the region.
*/
int scanCount;
private int scanCount;
/**
* A 16-bit signed integer that defines the maximum number of points in any one scan in this region.
*/
int maxScan;
private int maxScan;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* lower-right corner of the rectangle.
*/
int bottom;
private int bottom;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* lower-right corner of the rectangle.
*/
int right;
private int right;
/**
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
* upper-left corner of the rectangle.
*/
int top;
private int top;
/**
* A 16-bit signed integer that defines the x-coordinate, in logical units, of the
* upper-left corner of the rectangle.
*/
int left;
private int left;
/**
* An array of Scan objects that define the scanlines in the region.
*/
WmfScanObject scanObjects[];
private WmfScanObject scanObjects[];
@Override
public HwmfRecordType getRecordType() {
return HwmfRecordType.createRegion;
}
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
nextInChain = leis.readShort();
objectType = leis.readShort();
@ -545,5 +639,10 @@ public class HwmfWindowing {
return 20 + size;
}
@Override
public void draw(HwmfGraphics ctx) {
}
}
}

View File

@ -17,6 +17,8 @@
package org.apache.poi.hwmf.usermodel;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@ -37,7 +39,8 @@ public class HwmfPicture {
}
public HwmfPicture(InputStream inputStream) throws IOException {
LittleEndianInputStream leis = new LittleEndianInputStream(inputStream);
BufferedInputStream bis = new BufferedInputStream(inputStream, 10000);
LittleEndianInputStream leis = new LittleEndianInputStream(bis);
HwmfPlaceableHeader placeableHeader = HwmfPlaceableHeader.readHeader(leis);
HwmfHeader header = new HwmfHeader(leis);
@ -65,8 +68,15 @@ public class HwmfPicture {
}
consumedSize += wr.init(leis, recordSize, recordFunction);
if (consumedSize < recordSize) {
leis.skip(recordSize - consumedSize);
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);
}
}
}

View File

@ -19,14 +19,27 @@ 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.IOException;
import java.util.List;
import java.util.Locale;
import javax.imageio.ImageIO;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord;
import org.apache.poi.hwmf.record.HwmfRecord;
import org.apache.poi.hwmf.usermodel.HwmfPicture;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.sl.usermodel.SlideShow;
import org.apache.poi.sl.usermodel.SlideShowFactory;
import org.junit.Ignore;
import org.junit.Test;
public class TestHwmfParsing {
@ -39,4 +52,58 @@ public class TestHwmfParsing {
List<HwmfRecord> records = wmf.getRecords();
assertEquals(581, records.size());
}
@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;
File outdir = new File("build/ppt");
if (outputFiles) {
outdir.mkdirs();
}
int wmfIdx = 1;
for (File f : files) {
try {
SlideShow<?,?> ss = SlideShowFactory.create(f);
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++;
}
}
wmfIdx++;
}
ss.close();
} catch (Exception e) {
System.out.println(f+" ignored.");
}
}
}
}