diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java new file mode 100644 index 000000000..64ee4cc34 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java @@ -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; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java new file mode 100644 index 000000000..4a07cc330 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java @@ -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 propStack = new ArrayDeque(); + 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())); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmap16.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmap16.java index 52ea1d9f1..15495de01 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmap16.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmap16.java @@ -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> 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 * 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. *
- * 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. *
- * 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. *
- * 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. *
* 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. *
- * 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 colorList = new ArrayList(); @@ -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>(7-j))&1); - } - break; - case BI_BITCOUNT_2: - wr.setSample(pixel/headerWidth, pixel%headerWidth, 0, (v>>4)&15); - pixel++; - if (pixel> 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; } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java index 9a5288fea..ff11d4862 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java @@ -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; + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java index 81526dd0b..a65b7b45b 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java @@ -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 ((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(); + } } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java index af6703ef0..d1a99e641 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java @@ -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; } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfHatchStyle.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfHatchStyle.java index 18922bfe7..00108c4ac 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfHatchStyle.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfHatchStyle.java @@ -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; diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMapMode.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMapMode.java new file mode 100644 index 000000000..28a26a88f --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMapMode.java @@ -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; + } +} \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java index ef539fb20..be59c2dd8 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java @@ -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. - * + * * * * @@ -481,26 +541,37 @@ public class HwmfMisc { *
BrushStyle
*/ 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); } } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java index cbd0a6011..d55f9e454 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java @@ -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) { + + } } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java new file mode 100644 index 000000000..4e908ac39 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java @@ -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); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecord.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecord.java index d600b09e4..7fd5ba563 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecord.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecord.java @@ -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); } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfTernaryRasterOp.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfTernaryRasterOp.java index 315eb1161..5ee3100fc 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfTernaryRasterOp.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfTernaryRasterOp.java @@ -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; diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java index 84dc4b1c0..6d332607f 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java @@ -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= 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); } } } diff --git a/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java b/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java index ac5d6c016..a1f2cbf1d 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java +++ b/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java @@ -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 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."); + } + } + } + }