import java.awt.image.BufferedImage;\r
import java.awt.image.ColorModel;\r
import java.awt.image.WritableRaster;\r
+import java.util.List;\r
\r
import org.apache.poi.hwmf.record.HwmfBrushStyle;\r
import org.apache.poi.hwmf.record.HwmfColorRef;\r
+import org.apache.poi.hwmf.record.HwmfFont;\r
import org.apache.poi.hwmf.record.HwmfFill.WmfSetPolyfillMode.HwmfPolyfillMode;\r
import org.apache.poi.hwmf.record.HwmfHatchStyle;\r
import org.apache.poi.hwmf.record.HwmfMapMode;\r
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;\r
+import org.apache.poi.hwmf.record.HwmfPalette.PaletteEntry;\r
import org.apache.poi.hwmf.record.HwmfPenStyle;\r
+import org.apache.poi.hwmf.record.HwmfText.HwmfTextAlignment;\r
+import org.apache.poi.hwmf.record.HwmfText.HwmfTextVerticalAlignment;\r
\r
public class HwmfDrawProperties {\r
private final Rectangle2D window;\r
private HwmfBkMode bkMode = HwmfBkMode.OPAQUE;\r
private HwmfPolyfillMode polyfillMode = HwmfPolyfillMode.WINDING;\r
private Shape region = null;\r
+ private List<PaletteEntry> palette = null;\r
+ private int paletteOffset = 0;\r
+ private HwmfFont font = null;\r
+ private HwmfColorRef textColor = new HwmfColorRef(Color.BLACK);\r
+ private HwmfTextAlignment textAlignLatin = HwmfTextAlignment.LEFT;\r
+ private HwmfTextVerticalAlignment textVAlignLatin = HwmfTextVerticalAlignment.TOP;\r
+ private HwmfTextAlignment textAlignAsian = HwmfTextAlignment.RIGHT;\r
+ private HwmfTextVerticalAlignment textVAlignAsian = HwmfTextVerticalAlignment.TOP;\r
\r
public HwmfDrawProperties() {\r
window = new Rectangle2D.Double(0, 0, 1, 1);\r
} else if (other.region instanceof Area) {\r
this.region = new Area(other.region);\r
}\r
+ this.palette = other.palette;\r
+ this.paletteOffset = other.paletteOffset;\r
+ this.font = other.font;\r
+ this.textColor = (other.textColor == null) ? null : other.textColor.clone();\r
}\r
\r
public void setViewportExt(double width, double height) {\r
+ if (viewport == null) {\r
+ viewport = (Rectangle2D)window.clone();\r
+ }\r
double x = viewport.getX();\r
double y = viewport.getY();\r
double w = (width != 0) ? width : viewport.getWidth();\r
public void setRegion(Shape region) {\r
this.region = region;\r
}\r
+\r
+ /**\r
+ * Returns the current palette.\r
+ * Callers may modify the palette.\r
+ * \r
+ * @return the current palette or null, if it hasn't been set\r
+ */\r
+ public List<PaletteEntry> getPalette() {\r
+ return palette;\r
+ }\r
+\r
+ /**\r
+ * Sets the current palette.\r
+ * It's the callers duty to set a modifiable copy of the palette.\r
+ *\r
+ * @param palette\r
+ */\r
+ public void setPalette(List<PaletteEntry> palette) {\r
+ this.palette = palette;\r
+ }\r
+\r
+ public int getPaletteOffset() {\r
+ return paletteOffset;\r
+ }\r
+\r
+ public void setPaletteOffset(int paletteOffset) {\r
+ this.paletteOffset = paletteOffset;\r
+ }\r
+\r
+ public HwmfColorRef getTextColor() {\r
+ return textColor;\r
+ }\r
+\r
+ public void setTextColor(HwmfColorRef textColor) {\r
+ this.textColor = textColor;\r
+ }\r
+\r
+ public HwmfFont getFont() {\r
+ return font;\r
+ }\r
+\r
+ public void setFont(HwmfFont font) {\r
+ this.font = font;\r
+ }\r
+\r
+ public HwmfTextAlignment getTextAlignLatin() {\r
+ return textAlignLatin;\r
+ }\r
+\r
+ public void setTextAlignLatin(HwmfTextAlignment textAlignLatin) {\r
+ this.textAlignLatin = textAlignLatin;\r
+ }\r
+\r
+ public HwmfTextVerticalAlignment getTextVAlignLatin() {\r
+ return textVAlignLatin;\r
+ }\r
+\r
+ public void setTextVAlignLatin(HwmfTextVerticalAlignment textVAlignLatin) {\r
+ this.textVAlignLatin = textVAlignLatin;\r
+ }\r
+\r
+ public HwmfTextAlignment getTextAlignAsian() {\r
+ return textAlignAsian;\r
+ }\r
+\r
+ public void setTextAlignAsian(HwmfTextAlignment textAlignAsian) {\r
+ this.textAlignAsian = textAlignAsian;\r
+ }\r
+\r
+ public HwmfTextVerticalAlignment getTextVAlignAsian() {\r
+ return textVAlignAsian;\r
+ }\r
+\r
+ public void setTextVAlignAsian(HwmfTextVerticalAlignment textVAlignAsian) {\r
+ this.textVAlignAsian = textVAlignAsian;\r
+ }\r
}\r
import java.awt.Rectangle;\r
import java.awt.Shape;\r
import java.awt.TexturePaint;\r
+import java.awt.font.TextAttribute;\r
import java.awt.geom.AffineTransform;\r
import java.awt.geom.GeneralPath;\r
import java.awt.geom.Rectangle2D;\r
import java.awt.image.BufferedImage;\r
+import java.text.AttributedString;\r
import java.util.ArrayList;\r
import java.util.LinkedList;\r
import java.util.List;\r
import java.util.NoSuchElementException;\r
\r
import org.apache.poi.hwmf.record.HwmfBrushStyle;\r
+import org.apache.poi.hwmf.record.HwmfFont;\r
import org.apache.poi.hwmf.record.HwmfHatchStyle;\r
import org.apache.poi.hwmf.record.HwmfMapMode;\r
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;\r
import org.apache.poi.hwmf.record.HwmfObjectTableEntry;\r
import org.apache.poi.hwmf.record.HwmfPenStyle;\r
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;\r
-import org.apache.poi.util.Units;\r
\r
public class HwmfGraphics {\r
private final Graphics2D graphicsCtx;\r
boolean dashAlt = ps.isAlternateDash();\r
// This value is not an integer index into the dash pattern array.\r
// Instead, it is a floating-point value that specifies a linear distance.\r
- float dashStart = (dashAlt && dashes.length > 1) ? dashes[0] : 0;\r
+ float dashStart = (dashAlt && dashes != null && dashes.length > 1) ? dashes[0] : 0;\r
\r
return new BasicStroke(width, cap, join, miterLimit, dashes, dashStart);\r
}\r
/**\r
* Restores the properties from the stack\r
*\r
- * @param index if the index is positive, the n-th element from the start is removed and activated.\r
- * If the index is negative, the n-th previous element relative to the current properties element is removed and activated.\r
+ * @param index if the index is positive, the n-th element from the start is activated.\r
+ * If the index is negative, the n-th previous element relative to the current properties element is activated.\r
*/\r
public void restoreProperties(int index) {\r
if (index == 0) {\r
int stackIndex = index;\r
if (stackIndex < 0) {\r
int curIdx = propStack.indexOf(prop);\r
- assert (curIdx != -1);\r
+ if (curIdx == -1) {\r
+ // the current element is not pushed to the stacked, i.e. it's the last\r
+ curIdx = propStack.size();\r
+ }\r
stackIndex = curIdx + index;\r
}\r
- prop = propStack.remove(stackIndex);\r
+ prop = propStack.get(stackIndex);\r
}\r
\r
+ /**\r
+ * After setting various window and viewport related properties,\r
+ * the underlying graphics context needs to be adapted.\r
+ * This methods gathers and sets the corresponding graphics transformations.\r
+ */\r
public void updateWindowMapMode() {\r
GraphicsConfiguration gc = graphicsCtx.getDeviceConfiguration();\r
Rectangle2D win = prop.getWindow();\r
switch (mapMode) {\r
default:\r
case MM_ANISOTROPIC:\r
- // scale output bounds to image bounds\r
- graphicsCtx.scale(gc.getBounds().getWidth()/bbox.getWidth(), gc.getBounds().getHeight()/bbox.getHeight());\r
- graphicsCtx.translate(-bbox.getX(), -bbox.getY());\r
-\r
// scale window bounds to output bounds\r
- graphicsCtx.translate(win.getCenterX(), win.getCenterY());\r
graphicsCtx.scale(bbox.getWidth()/win.getWidth(), bbox.getHeight()/win.getHeight());\r
- graphicsCtx.translate(-win.getCenterX(), -win.getCenterY());\r
+ graphicsCtx.translate(-win.getX(), -win.getY());\r
break;\r
case MM_ISOTROPIC:\r
// TODO: to be validated ...\r
// like anisotropic, but use x-axis as reference\r
- graphicsCtx.scale(gc.getBounds().getWidth()/bbox.getWidth(), gc.getBounds().getWidth()/bbox.getWidth());\r
- graphicsCtx.translate(-bbox.getX(), -bbox.getY());\r
- graphicsCtx.translate(win.getCenterX(), win.getCenterY());\r
graphicsCtx.scale(bbox.getWidth()/win.getWidth(), bbox.getWidth()/win.getWidth());\r
- graphicsCtx.translate(-win.getCenterX(), -win.getCenterY());\r
+ graphicsCtx.translate(-win.getX(), -win.getY());\r
break;\r
case MM_LOMETRIC:\r
case MM_HIMETRIC:\r
// TODO: to be validated ...\r
graphicsCtx.transform(gc.getNormalizingTransform());\r
graphicsCtx.scale(1./mapMode.scale, -1./mapMode.scale);\r
- graphicsCtx.translate(-bbox.getX(), -bbox.getY());\r
+ graphicsCtx.translate(-win.getX(), -win.getY());\r
break;\r
case MM_TEXT:\r
// TODO: to be validated ...\r
break;\r
}\r
}\r
+ \r
+ public void drawString(String text, Rectangle2D bounds) {\r
+ HwmfFont font = prop.getFont();\r
+ if (font == null) {\r
+ return;\r
+ }\r
+ AttributedString as = new AttributedString(text);\r
+ as.addAttribute(TextAttribute.FAMILY, font.getFacename());\r
+ // TODO: fix font height calculation\r
+ as.addAttribute(TextAttribute.SIZE, Math.abs(font.getHeight()));\r
+ as.addAttribute(TextAttribute.STRIKETHROUGH, font.isStrikeOut());\r
+ if (font.isUnderline()) {\r
+ as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);\r
+ }\r
+ if (font.isItalic()) {\r
+ as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);\r
+ }\r
+ as.addAttribute(TextAttribute.WEIGHT, font.getWeight());\r
+ \r
+ double angle = Math.toRadians(-font.getEscapement()/10.);\r
+ \r
+ \r
+ final AffineTransform at = graphicsCtx.getTransform();\r
+ try {\r
+ graphicsCtx.translate(bounds.getX(), bounds.getY());\r
+ graphicsCtx.rotate(angle);\r
+ if (prop.getBkMode() == HwmfBkMode.OPAQUE) {\r
+ // TODO: validate bounds\r
+ graphicsCtx.setBackground(prop.getBackgroundColor().getColor());\r
+ graphicsCtx.fill(bounds);\r
+ }\r
+ graphicsCtx.setColor(prop.getTextColor().getColor());\r
+ graphicsCtx.drawString(as.getIterator(), 0, 0); // (float)bounds.getX(), (float)bounds.getY());\r
+ } finally {\r
+ graphicsCtx.setTransform(at);\r
+ }\r
+ }\r
}\r
\r
package org.apache.poi.hwmf.record;\r
\r
-import java.awt.Polygon;\r
-import java.awt.Rectangle;\r
import java.awt.Shape;\r
import java.awt.geom.Arc2D;\r
+import java.awt.geom.Area;\r
import java.awt.geom.Ellipse2D;\r
-import java.awt.geom.GeneralPath;\r
import java.awt.geom.Line2D;\r
+import java.awt.geom.Path2D;\r
import java.awt.geom.Point2D;\r
import java.awt.geom.Rectangle2D;\r
import java.awt.geom.RoundRectangle2D;\r
import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
\r
import org.apache.poi.hwmf.draw.HwmfGraphics;\r
import org.apache.poi.util.LittleEndianConsts;\r
*/\r
public static class WmfPolygon implements HwmfRecord {\r
\r
- /**\r
- * A 16-bit signed integer that defines the number of points in the array.\r
- */\r
- private int numberofPoints;\r
-\r
- short xPoints[], yPoints[];\r
-\r
+ private Path2D poly = new Path2D.Double();\r
+ \r
@Override\r
public HwmfRecordType getRecordType() {\r
return HwmfRecordType.polygon;\r
\r
@Override\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
- numberofPoints = leis.readShort();\r
- xPoints = new short[numberofPoints];\r
- yPoints = new short[numberofPoints];\r
+ /**\r
+ * A 16-bit signed integer that defines the number of points in the array.\r
+ */\r
+ int numberofPoints = leis.readShort();\r
\r
for (int i=0; i<numberofPoints; i++) {\r
// A 16-bit signed integer that defines the horizontal (x) coordinate of the point.\r
- xPoints[i] = leis.readShort();\r
+ int x = leis.readShort();\r
// A 16-bit signed integer that defines the vertical (y) coordinate of the point.\r
- yPoints[i] = leis.readShort();\r
+ int y = leis.readShort();\r
+ if (i==0) {\r
+ poly.moveTo(x, y);\r
+ } else {\r
+ poly.lineTo(x, y);\r
+ }\r
}\r
\r
return LittleEndianConsts.SHORT_SIZE+numberofPoints*LittleEndianConsts.INT_SIZE;\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
- ctx.fill(getShape());\r
+ Path2D p = getShape();\r
+ p.closePath();\r
+ p.setWindingRule(ctx.getProperties().getPolyfillMode().awtFlag);\r
+ ctx.fill(p);\r
}\r
\r
- protected Polygon getShape() {\r
- Polygon polygon = new Polygon();\r
- for(int i = 0; i < numberofPoints; i++) {\r
- polygon.addPoint(xPoints[i], yPoints[i]);\r
- }\r
- return polygon;\r
+ protected Path2D getShape() {\r
+ return (Path2D)poly.clone();\r
}\r
}\r
\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
- ctx.draw(getShape());\r
+ Path2D p = getShape();\r
+ p.setWindingRule(ctx.getProperties().getPolyfillMode().awtFlag);\r
+ ctx.draw(p);\r
}\r
}\r
\r
* A 16-bit unsigned integer used to index into the WMF Object Table to get\r
* the region to be framed.\r
*/\r
- private int region;\r
+ private int regionIndex;\r
/**\r
* A 16-bit unsigned integer used to index into the WMF Object Table to get the\r
* Brush to use for filling the region.\r
*/\r
- private int brush;\r
+ private int brushIndex;\r
/**\r
* A 16-bit signed integer that defines the height, in logical units, of the\r
* region frame.\r
\r
@Override\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
- region = leis.readUShort();\r
- brush = leis.readUShort();\r
+ regionIndex = leis.readUShort();\r
+ brushIndex = leis.readUShort();\r
height = leis.readShort();\r
width = leis.readShort();\r
return 4*LittleEndianConsts.SHORT_SIZE;\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ ctx.applyObjectTableEntry(brushIndex);\r
+ ctx.applyObjectTableEntry(regionIndex);\r
+ Rectangle2D inner = ctx.getProperties().getRegion().getBounds();\r
+ double x = inner.getX()-width;\r
+ double y = inner.getY()-height;\r
+ double w = inner.getWidth()+2*width;\r
+ double h = inner.getHeight()+2*height;\r
+ Rectangle2D outer = new Rectangle2D.Double(x,y,w,h);\r
+ Area frame = new Area(outer);\r
+ frame.subtract(new Area(inner));\r
+ ctx.fill(frame);\r
}\r
}\r
\r
*/\r
public static class WmfPolyPolygon implements HwmfRecord {\r
\r
- /**\r
- * A 16-bit unsigned integer that defines the number of polygons in the object.\r
- */\r
- private int numberOfPolygons;\r
-\r
- /**\r
- * A NumberOfPolygons array of 16-bit unsigned integers that define the number of\r
- * points for each polygon in the object.\r
- */\r
- private int pointsPerPolygon[];\r
-\r
- /**\r
- * An array of 16-bit unsigned integers that define the coordinates of the polygons.\r
- */\r
- private int xPoints[][];\r
-\r
- /**\r
- * An array of 16-bit unsigned integers that define the coordinates of the polygons.\r
- */\r
- private int yPoints[][];\r
-\r
-\r
+ private List<Path2D> polyList = new ArrayList<Path2D>();\r
+ \r
@Override\r
public HwmfRecordType getRecordType() {\r
return HwmfRecordType.polyPolygon;\r
@Override\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
// see http://secunia.com/gfx/pdf/SA31675_BA.pdf ;)\r
- numberOfPolygons = leis.readUShort();\r
- pointsPerPolygon = new int[numberOfPolygons];\r
- xPoints = new int[numberOfPolygons][];\r
- yPoints = new int[numberOfPolygons][];\r
+ /**\r
+ * A 16-bit unsigned integer that defines the number of polygons in the object.\r
+ */\r
+ int numberOfPolygons = leis.readUShort();\r
+ /**\r
+ * A NumberOfPolygons array of 16-bit unsigned integers that define the number of\r
+ * points for each polygon in the object.\r
+ */\r
+ int[] pointsPerPolygon = new int[numberOfPolygons];\r
\r
int size = LittleEndianConsts.SHORT_SIZE;\r
\r
size += LittleEndianConsts.SHORT_SIZE;\r
}\r
\r
- for (int i=0; i<numberOfPolygons; i++) {\r
-\r
- xPoints[i] = new int[pointsPerPolygon[i]];\r
- yPoints[i] = new int[pointsPerPolygon[i]];\r
-\r
- for (int j=0; j<pointsPerPolygon[i]; j++) {\r
- xPoints[i][j] = leis.readUShort();\r
- yPoints[i][j] = leis.readUShort();\r
+ for (int nPoints : pointsPerPolygon) {\r
+ /**\r
+ * An array of 16-bit unsigned integers that define the coordinates of the polygons.\r
+ */\r
+ Path2D poly = new Path2D.Double();\r
+ for (int i=0; i<nPoints; i++) {\r
+ int x = leis.readUShort();\r
+ int y = leis.readUShort();\r
size += 2*LittleEndianConsts.SHORT_SIZE;\r
+ if (i == 0) {\r
+ poly.moveTo(x, y);\r
+ } else {\r
+ poly.lineTo(x, y);\r
+ }\r
}\r
+ poly.closePath();\r
+ polyList.add(poly);\r
}\r
\r
return size;\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ int windingRule = ctx.getProperties().getPolyfillMode().awtFlag;\r
+ Area area = new Area();\r
+ for (Path2D poly : polyList) {\r
+ Path2D p = (Path2D)poly.clone();\r
+ p.setWindingRule(windingRule);\r
+ area.add(new Area(p));\r
+ }\r
+ ctx.draw(area);\r
}\r
}\r
\r
BufferedImage getImage();\r
}\r
\r
+ /**\r
+ * The ColorUsage Enumeration (a 16-bit unsigned integer) specifies whether a color table\r
+ * exists in a device-independent bitmap (DIB) and how to interpret its values,\r
+ * i.e. if contains explicit RGB values or indexes into a palette.\r
+ */\r
+ public enum ColorUsage {\r
+ /**\r
+ * The color table contains RGB values\r
+ */\r
+ DIB_RGB_COLORS(0x0000),\r
+ /**\r
+ * The color table contains 16-bit indices into the current logical palette in\r
+ * the playback device context.\r
+ */\r
+ DIB_PAL_COLORS(0x0001),\r
+ /**\r
+ * No color table exists. The pixels in the DIB are indices into the current\r
+ * logical palette in the playback device context.\r
+ */\r
+ DIB_PAL_INDICES(0x0002)\r
+ ;\r
+\r
+\r
+ int flag;\r
+ ColorUsage(int flag) {\r
+ this.flag = flag;\r
+ }\r
+\r
+ static ColorUsage valueOf(int flag) {\r
+ for (ColorUsage bs : values()) {\r
+ if (bs.flag == flag) return bs;\r
+ }\r
+ return null;\r
+ }\r
+ }\r
+ \r
\r
/**\r
* The META_FILLREGION record fills a region using a specified brush.\r
/**\r
* A 16-bit unsigned integer that defines whether the Colors field of the\r
* DIB contains explicit RGB values or indexes into a palette.\r
- * This value MUST be in the ColorUsage Enumeration:\r
- * DIB_RGB_COLORS = 0x0000,\r
- * DIB_PAL_COLORS = 0x0001,\r
- * DIB_PAL_INDICES = 0x0002\r
*/\r
- private int colorUsage;\r
+ private ColorUsage colorUsage;\r
/**\r
* A 16-bit signed integer that defines the height, in logical units, of the\r
* source rectangle.\r
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);\r
assert(rasterOpCode == rasterOperation.opCode);\r
\r
- colorUsage = leis.readUShort();\r
+ colorUsage = ColorUsage.valueOf(leis.readUShort());\r
srcHeight = leis.readShort();\r
srcWidth = leis.readShort();\r
ySrc = leis.readShort();\r
/**\r
* A 16-bit unsigned integer that defines whether the Colors field of the\r
* DIB contains explicit RGB values or indexes into a palette.\r
- * This MUST be one of the values in the ColorUsage Enumeration:\r
- * DIB_RGB_COLORS = 0x0000,\r
- * DIB_PAL_COLORS = 0x0001,\r
- * DIB_PAL_INDICES = 0x0002\r
*/\r
- private int colorUsage; \r
+ private ColorUsage colorUsage; \r
/**\r
* A 16-bit unsigned integer that defines the number of scan lines in the source.\r
*/\r
\r
@Override\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
- colorUsage = leis.readUShort();\r
+ colorUsage = ColorUsage.valueOf(leis.readUShort());\r
scanCount = leis.readUShort();\r
startScan = leis.readUShort();\r
yDib = leis.readUShort();\r
\r
return 5*LittleEndianConsts.SHORT_SIZE+8*LittleEndianConsts.BYTE_SIZE+readBytes;\r
}\r
+\r
+ public int getHeight() {\r
+ return height;\r
+ }\r
+\r
+ public int getWidth() {\r
+ return width;\r
+ }\r
+\r
+ public int getEscapement() {\r
+ return escapement;\r
+ }\r
+\r
+ public int getOrientation() {\r
+ return orientation;\r
+ }\r
+\r
+ public int getWeight() {\r
+ return weight;\r
+ }\r
+\r
+ public boolean isItalic() {\r
+ return italic;\r
+ }\r
+\r
+ public boolean isUnderline() {\r
+ return underline;\r
+ }\r
+\r
+ public boolean isStrikeOut() {\r
+ return strikeOut;\r
+ }\r
+\r
+ public WmfCharset getCharSet() {\r
+ return charSet;\r
+ }\r
+\r
+ public WmfOutPrecision getOutPrecision() {\r
+ return outPrecision;\r
+ }\r
+\r
+ public WmfClipPrecision getClipPrecision() {\r
+ return clipPrecision;\r
+ }\r
+\r
+ public WmfFontQuality getQuality() {\r
+ return quality;\r
+ }\r
+\r
+ public WmfFontFamilyClass getFamily() {\r
+ return family;\r
+ }\r
+\r
+ public WmfFontPitch getPitch() {\r
+ return pitch;\r
+ }\r
+\r
+ public String getFacename() {\r
+ return facename;\r
+ }\r
}\r
\r
import org.apache.poi.hwmf.draw.HwmfDrawProperties;\r
import org.apache.poi.hwmf.draw.HwmfGraphics;\r
+import org.apache.poi.hwmf.record.HwmfFill.ColorUsage;\r
import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord;\r
import org.apache.poi.util.LittleEndianConsts;\r
import org.apache.poi.util.LittleEndianInputStream;\r
* If the Style field specifies BS_PATTERN, a ColorUsage value of DIB_RGB_COLORS MUST be\r
* used regardless of the contents of this field.\r
*\r
- * If the Style field specified anything but BS_PATTERN, this field MUST be one of the values:\r
- * DIB_RGB_COLORS = 0x0000,\r
- * DIB_PAL_COLORS = 0x0001,\r
- * DIB_PAL_INDICES = 0x0002\r
+ * If the Style field specified anything but BS_PATTERN, this field MUST be one of the ColorUsage values.\r
*/\r
- private int colorUsage;\r
+ private ColorUsage colorUsage;\r
\r
private HwmfBitmapDib patternDib;\r
private HwmfBitmap16 pattern16;\r
@Override\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
style = HwmfBrushStyle.valueOf(leis.readUShort());\r
- colorUsage = leis.readUShort();\r
+ colorUsage = ColorUsage.valueOf(leis.readUShort());\r
int size = 2*LittleEndianConsts.SHORT_SIZE;\r
switch (style) {\r
case BS_SOLID:\r
\r
import java.awt.Color;\r
import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
\r
+import org.apache.poi.hwmf.draw.HwmfDrawProperties;\r
import org.apache.poi.hwmf.draw.HwmfGraphics;\r
+import org.apache.poi.util.BitField;\r
+import org.apache.poi.util.BitFieldFactory;\r
import org.apache.poi.util.LittleEndianConsts;\r
import org.apache.poi.util.LittleEndianInputStream;\r
\r
public class HwmfPalette {\r
- \r
+\r
public static class PaletteEntry {\r
- enum PaletteEntryFlag {\r
- /**\r
- * Specifies that the logical palette entry be used for palette animation. This value\r
- * prevents other windows from matching colors to the palette entry because the color frequently\r
- * changes. If an unused system-palette entry is available, the color is placed in that entry.\r
- * Otherwise, the color is not available for animation.\r
- */\r
- PC_RESERVED(0x01),\r
- /**\r
- * Specifies that the low-order word of the logical palette entry designates a hardware\r
- * palette index. This value allows the application to show the contents of the display device palette.\r
- */\r
- PC_EXPLICIT(0x02),\r
- /**\r
- * Specifies that the color be placed in an unused entry in the system palette\r
- * instead of being matched to an existing color in the system palette. If there are no unused entries\r
- * in the system palette, the color is matched normally. Once this color is in the system palette,\r
- * colors in other logical palettes can be matched to this color.\r
- */\r
- PC_NOCOLLAPSE(0x04)\r
- ;\r
- \r
- int flag;\r
- \r
- PaletteEntryFlag(int flag) {\r
- this.flag = flag;\r
- }\r
+ private static final BitField PC_RESERVED = BitFieldFactory.getInstance(0x01);\r
+ private static final BitField PC_EXPLICIT = BitFieldFactory.getInstance(0x02);\r
+ private static final BitField PC_NOCOLLAPSE = BitFieldFactory.getInstance(0x04);\r
\r
- static PaletteEntryFlag valueOf(int flag) {\r
- for (PaletteEntryFlag pef : values()) {\r
- if (pef.flag == flag) return pef;\r
- }\r
- return null;\r
- } \r
+ private int values;\r
+ private Color colorRef;\r
\r
+ private PaletteEntry() {\r
+ this.values = PC_RESERVED.set(0);\r
+ this.colorRef = Color.BLACK;\r
}\r
- \r
- // Values (1 byte): An 8-bit unsigned integer that defines how the palette entry is to be used. \r
- // The Values field MUST be 0x00 or one of the values in the PaletteEntryFlag Enumeration table.\r
- // Blue (1 byte): An 8-bit unsigned integer that defines the blue intensity value for the palette entry.\r
- // Green (1 byte): An 8-bit unsigned integer that defines the green intensity value for the palette entry.\r
- // Red (1 byte): An 8-bit unsigned integer that defines the red intensity value for the palette entry.\r
- private PaletteEntryFlag values;\r
- private Color colorRef;\r
- \r
+\r
+ private PaletteEntry(PaletteEntry other) {\r
+ this.values = other.values;\r
+ this.colorRef = other.colorRef;\r
+ }\r
+\r
public int init(LittleEndianInputStream leis) throws IOException {\r
- values = PaletteEntryFlag.valueOf(leis.readUByte());\r
+ // Values (1 byte): An 8-bit unsigned integer that defines how the palette entry is to be used.\r
+ // The Values field MUST be 0x00 or one of the values in the PaletteEntryFlag Enumeration table.\r
+ values = leis.readUByte();\r
+ // Blue (1 byte): An 8-bit unsigned integer that defines the blue intensity value for the palette entry.\r
int blue = leis.readUByte();\r
+ // Green (1 byte): An 8-bit unsigned integer that defines the green intensity value for the palette entry.\r
int green = leis.readUByte();\r
+ // Red (1 byte): An 8-bit unsigned integer that defines the red intensity value for the palette entry.\r
int red = leis.readUByte();\r
colorRef = new Color(red, green, blue);\r
- \r
+\r
return 4*LittleEndianConsts.BYTE_SIZE;\r
}\r
+\r
+ /**\r
+ * Specifies that the logical palette entry be used for palette animation. This value\r
+ * prevents other windows from matching colors to the palette entry because the color frequently\r
+ * changes. If an unused system-palette entry is available, the color is placed in that entry.\r
+ * Otherwise, the color is not available for animation.\r
+ */\r
+ public boolean isReserved() {\r
+ return PC_RESERVED.isSet(values);\r
+ }\r
+\r
+ /**\r
+ * Specifies that the low-order word of the logical palette entry designates a hardware\r
+ * palette index. This value allows the application to show the contents of the display device palette.\r
+ */\r
+ public boolean isExplicit() {\r
+ return PC_EXPLICIT.isSet(values);\r
+ }\r
+\r
+ /**\r
+ * Specifies that the color be placed in an unused entry in the system palette\r
+ * instead of being matched to an existing color in the system palette. If there are no unused entries\r
+ * in the system palette, the color is matched normally. Once this color is in the system palette,\r
+ * colors in other logical palettes can be matched to this color.\r
+ */\r
+ public boolean isNoCollapse() {\r
+ return PC_NOCOLLAPSE.isSet(values);\r
+ }\r
}\r
- \r
+\r
public static abstract class WmfPaletteParent implements HwmfRecord {\r
- \r
+\r
/**\r
* Start (2 bytes): A 16-bit unsigned integer that defines the offset into the Palette Object when\r
* used with the META_SETPALENTRIES and META_ANIMATEPALETTE record types.\r
* When used with META_CREATEPALETTE, it MUST be 0x0300\r
*/\r
private int start;\r
- \r
- /**\r
- * NumberOfEntries (2 bytes): A 16-bit unsigned integer that defines the number of objects in\r
- * aPaletteEntries. \r
- */\r
- private int numberOfEntries;\r
- \r
- private PaletteEntry entries[];\r
- \r
+\r
+ private List<PaletteEntry> palette = new ArrayList<PaletteEntry>();\r
+\r
@Override\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
start = leis.readUShort();\r
- numberOfEntries = leis.readUShort();\r
+ /**\r
+ * NumberOfEntries (2 bytes): A 16-bit unsigned integer that defines the number of objects in\r
+ * aPaletteEntries.\r
+ */\r
+ int numberOfEntries = leis.readUShort();\r
int size = 2*LittleEndianConsts.SHORT_SIZE;\r
- entries = new PaletteEntry[numberOfEntries];\r
for (int i=0; i<numberOfEntries; i++) {\r
- entries[i] = new PaletteEntry();\r
- size += entries[i].init(leis);\r
+ PaletteEntry pe = new PaletteEntry();\r
+ size += pe.init(leis);\r
+ palette.add(pe);\r
}\r
return size;\r
}\r
\r
- @Override\r
- public void draw(HwmfGraphics ctx) {\r
+ protected List<PaletteEntry> getPaletteCopy() {\r
+ List<PaletteEntry> newPalette = new ArrayList<PaletteEntry>();\r
+ for (PaletteEntry et : palette) {\r
+ newPalette.add(new PaletteEntry(et));\r
+ }\r
+ return newPalette;\r
+ }\r
\r
+ protected int getPaletteStart() {\r
+ return start;\r
}\r
}\r
- \r
+\r
/**\r
* The META_CREATEPALETTE record creates a Palette Object\r
*/\r
public void draw(HwmfGraphics ctx) {\r
ctx.addObjectTableEntry(this);\r
}\r
- \r
+\r
@Override\r
public void applyObject(HwmfGraphics ctx) {\r
-\r
+ ctx.getProperties().setPalette(getPaletteCopy());\r
}\r
}\r
\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ HwmfDrawProperties props = ctx.getProperties();\r
+ List<PaletteEntry> palette = props.getPalette();\r
+ if (palette == null) {\r
+ palette = new ArrayList<PaletteEntry>();\r
+ }\r
+ int start = getPaletteStart();\r
+ for (int i=palette.size(); i<start; i++) {\r
+ palette.add(new PaletteEntry());\r
+ }\r
+ int index = start;\r
+ for (PaletteEntry palCopy : getPaletteCopy()) {\r
+ if (palette.size() <= index) {\r
+ palette.add(palCopy);\r
+ } else {\r
+ palette.set(index, palCopy);\r
+ }\r
+ index++;\r
+ }\r
+ props.setPalette(palette);\r
}\r
}\r
- \r
+\r
/**\r
* The META_RESIZEPALETTE record redefines the size of the logical palette that is defined in the\r
* playback device context.\r
*/\r
public static class WmfResizePalette implements HwmfRecord {\r
/**\r
- * A 16-bit unsigned integer that defines the number of entries in \r
+ * A 16-bit unsigned integer that defines the number of entries in\r
* the logical palette.\r
*/\r
int numberOfEntries;\r
- \r
+\r
@Override\r
public HwmfRecordType getRecordType() {\r
return HwmfRecordType.resizePalette;\r
}\r
- \r
+\r
@Override\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
numberOfEntries = leis.readUShort();\r
return LittleEndianConsts.SHORT_SIZE;\r
- } \r
+ }\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ HwmfDrawProperties props = ctx.getProperties();\r
+ List<PaletteEntry> palette = props.getPalette();\r
+ if (palette == null) {\r
+ palette = new ArrayList<PaletteEntry>();\r
+ }\r
+ for (int i=palette.size(); i<numberOfEntries; i++) {\r
+ palette.add(new PaletteEntry());\r
+ }\r
+ palette = palette.subList(0, numberOfEntries);\r
+ props.setPalette(palette);\r
}\r
}\r
\r
* A 16-bit unsigned integer used to index into the WMF Object Table to get\r
* the Palette Object to be selected.\r
*/\r
- private int palette;\r
+ private int paletteIndex;\r
\r
@Override\r
public HwmfRecordType getRecordType() {\r
return HwmfRecordType.selectPalette;\r
}\r
- \r
+\r
@Override\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
- palette = leis.readUShort();\r
+ paletteIndex = leis.readUShort();\r
return LittleEndianConsts.SHORT_SIZE;\r
- } \r
+ }\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ ctx.applyObjectTableEntry(paletteIndex);\r
}\r
}\r
\r
/**\r
* The META_ANIMATEPALETTE record redefines entries in the logical palette that\r
* is defined in the playback device context with the specified Palette object\r
- * \r
+ *\r
* The logical palette that is specified by the Palette object in this record is the\r
* source of the palette changes, and the logical palette that is currently selected\r
* into the playback device context is the destination. Entries in the destination\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ HwmfDrawProperties props = ctx.getProperties();\r
+ List<PaletteEntry> dest = props.getPalette();\r
+ List<PaletteEntry> src = getPaletteCopy();\r
+ int start = getPaletteStart();\r
+ if (dest == null) {\r
+ dest = new ArrayList<PaletteEntry>();\r
+ }\r
+ for (int i=dest.size(); i<start; i++) {\r
+ dest.add(new PaletteEntry());\r
+ }\r
+ for (int i=0; i<src.size(); i++) {\r
+ PaletteEntry pe = src.get(i);\r
+ if (dest.size() <= start+i) {\r
+ dest.add(pe);\r
+ } else {\r
+ PaletteEntry peDst = dest.get(start+i);\r
+ if (peDst.isReserved()) {\r
+ dest.set(start+i, pe);\r
+ }\r
+ }\r
+ }\r
+ props.setPalette(dest);\r
}\r
}\r
}\r
/**\r
* The pen is solid.\r
*/\r
- SOLID(0x0000, 10),\r
+ SOLID(0x0000, null),\r
/**\r
* The pen is dashed. (-----) \r
*/\r
/**\r
* The pen is invisible.\r
*/\r
- NULL(0x0005),\r
+ NULL(0x0005, null),\r
/**\r
* The pen is solid. When this pen is used in any drawing record that takes a\r
* bounding rectangle, the dimensions of the figure are shrunk so that it fits\r
* entirely in the bounding rectangle, taking into account the width of the pen.\r
*/\r
- INSIDEFRAME(0x0006, 10),\r
+ INSIDEFRAME(0x0006, null),\r
/**\r
* The pen uses a styling array supplied by the user.\r
* (this is currently not supported and drawn as solid ... no idea where the user\r
* styling is supposed to come from ...)\r
*/\r
- USERSTYLE(0x0007, 10);\r
+ USERSTYLE(0x0007, null);\r
\r
\r
public int wmfFlag;\r
public static int WMF_HEADER_MAGIC = 0x9AC6CDD7;\r
\r
final Rectangle2D bounds;\r
+ final int unitsPerInch;\r
\r
protected HwmfPlaceableHeader(LittleEndianInputStream leis) throws IOException {\r
/*\r
* Thus, a value of 720 specifies that the image SHOULD be rendered at twice its normal size,\r
* and a value of 2880 specifies that the image SHOULD be rendered at half its normal size.\r
*/\r
- int inch = leis.readShort();\r
+ unitsPerInch = leis.readShort();\r
\r
/*\r
* Reserved (4 bytes): A field that is not used and MUST be set to 0x00000000.\r
public Rectangle2D getBounds() {\r
return bounds;\r
}\r
+\r
+ public int getUnitsPerInch() {\r
+ return unitsPerInch;\r
+ }\r
}\r
\r
package org.apache.poi.hwmf.record;\r
\r
+import java.awt.geom.Rectangle2D;\r
import java.io.IOException;\r
+import java.text.AttributedString;\r
\r
+import org.apache.poi.hwmf.draw.HwmfDrawProperties;\r
import org.apache.poi.hwmf.draw.HwmfGraphics;\r
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode;\r
+import org.apache.poi.util.BitField;\r
+import org.apache.poi.util.BitFieldFactory;\r
import org.apache.poi.util.LittleEndianConsts;\r
import org.apache.poi.util.LittleEndianInputStream;\r
import org.apache.poi.util.LocaleUtil;\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ ctx.getProperties().setTextColor(colorRef);\r
}\r
}\r
\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ Rectangle2D bounds = new Rectangle2D.Double(xStart, yStart, 0, 0);\r
+ ctx.drawString(text, bounds);\r
}\r
}\r
\r
}\r
}\r
\r
-\r
+ public enum HwmfTextAlignment {\r
+ LEFT,\r
+ RIGHT,\r
+ CENTER;\r
+ }\r
\r
+ public enum HwmfTextVerticalAlignment {\r
+ TOP,\r
+ BOTTOM,\r
+ BASELINE;\r
+ }\r
\r
/**\r
* The META_SETTEXTALIGN record defines text-alignment values in the playback device context.\r
*/\r
public static class WmfSetTextAlign implements HwmfRecord {\r
\r
- /**\r
- * A 16-bit unsigned integer that defines text alignment.\r
- * This value MUST be a combination of one or more TextAlignmentMode Flags\r
- * for text with a horizontal baseline, and VerticalTextAlignmentMode Flags\r
- * for text with a vertical baseline.\r
- * \r
- * TextAlignmentMode Flags:\r
- * TA_NOUPDATECP (0x0000):\r
+ // ***********************************************************************************\r
+ // TextAlignmentMode Flags:\r
+ // ***********************************************************************************\r
+ \r
+ /** \r
* The drawing position in the playback device context MUST NOT be updated after each\r
* text output call. The reference point MUST be passed to the text output function.\r
- * \r
- * TA_LEFT (0x0000):\r
+ */\r
+ @SuppressWarnings("unused")\r
+ private static final BitField TA_NOUPDATECP = BitFieldFactory.getInstance(0x0000);\r
+ \r
+ /**\r
* The reference point MUST be on the left edge of the bounding rectangle.\r
- * \r
- * TA_TOP (0x0000):\r
+ */\r
+ @SuppressWarnings("unused")\r
+ private static final BitField TA_LEFT = BitFieldFactory.getInstance(0x0000);\r
+ \r
+ /**\r
* The reference point MUST be on the top edge of the bounding rectangle.\r
- * \r
- * TA_UPDATECP (0x0001):\r
+ */\r
+ @SuppressWarnings("unused")\r
+ private static final BitField TA_TOP = BitFieldFactory.getInstance(0x0000);\r
+ \r
+ /**\r
* The drawing position in the playback device context MUST be updated after each text\r
* output call. It MUST be used as the reference point.\r
- * \r
- * TA_RIGHT (0x0002):\r
+ */\r
+ @SuppressWarnings("unused")\r
+ private static final BitField TA_UPDATECP = BitFieldFactory.getInstance(0x0001);\r
+ \r
+ /**\r
* The reference point MUST be on the right edge of the bounding rectangle.\r
- * \r
- * TA_CENTER (0x0006):\r
+ */\r
+ private static final BitField TA_RIGHT = BitFieldFactory.getInstance(0x0002);\r
+ \r
+ /**\r
* The reference point MUST be aligned horizontally with the center of the bounding\r
* rectangle.\r
- * \r
- * TA_BOTTOM (0x0008):\r
+ */\r
+ private static final BitField TA_CENTER = BitFieldFactory.getInstance(0x0006);\r
+ \r
+ /**\r
* The reference point MUST be on the bottom edge of the bounding rectangle.\r
- * \r
- * TA_BASELINE (0x0018):\r
+ */\r
+ private static final BitField TA_BOTTOM = BitFieldFactory.getInstance(0x0008);\r
+ \r
+ /**\r
* The reference point MUST be on the baseline of the text.\r
- * \r
- * TA_RTLREADING (0x0100):\r
+ */\r
+ private static final BitField TA_BASELINE = BitFieldFactory.getInstance(0x0018);\r
+ \r
+ /**\r
* The text MUST be laid out in right-to-left reading order, instead of the default\r
- * left-toright order. This SHOULD be applied only when the font that is defined in the\r
+ * left-to-right order. This SHOULD be applied only when the font that is defined in the\r
* playback device context is either Hebrew or Arabic.\r
- * \r
- * \r
- * VerticalTextAlignmentMode Flags (e.g. for Kanji fonts)\r
- * VTA_TOP (0x0000):\r
+ */\r
+ @SuppressWarnings("unused")\r
+ private static final BitField TA_RTLREADING = BitFieldFactory.getInstance(0x0100);\r
+ \r
+ // ***********************************************************************************\r
+ // VerticalTextAlignmentMode Flags (e.g. for Kanji fonts)\r
+ // ***********************************************************************************\r
+ \r
+ /**\r
* The reference point MUST be on the top edge of the bounding rectangle.\r
- * \r
- * VTA_RIGHT (0x0000):\r
+ */\r
+ @SuppressWarnings("unused")\r
+ private static final BitField VTA_TOP = BitFieldFactory.getInstance(0x0000);\r
+ \r
+ /**\r
* The reference point MUST be on the right edge of the bounding rectangle.\r
- * \r
- * VTA_BOTTOM (0x0002):\r
+ */\r
+ @SuppressWarnings("unused")\r
+ private static final BitField VTA_RIGHT = BitFieldFactory.getInstance(0x0000);\r
+ \r
+ /**\r
* The reference point MUST be on the bottom edge of the bounding rectangle.\r
- * \r
- * VTA_CENTER (0x0006):\r
+ */\r
+ private static final BitField VTA_BOTTOM = BitFieldFactory.getInstance(0x0002);\r
+ \r
+ /**\r
* The reference point MUST be aligned vertically with the center of the bounding\r
* rectangle.\r
- * \r
- * VTA_LEFT (0x0008):\r
+ */\r
+ private static final BitField VTA_CENTER = BitFieldFactory.getInstance(0x0006);\r
+ \r
+ /**\r
* The reference point MUST be on the left edge of the bounding rectangle.\r
- * \r
- * VTA_BASELINE (0x0018):\r
+ */\r
+ private static final BitField VTA_LEFT = BitFieldFactory.getInstance(0x0008);\r
+ \r
+ /**\r
* The reference point MUST be on the baseline of the text.\r
*/\r
+ private static final BitField VTA_BASELINE = BitFieldFactory.getInstance(0x0018);\r
+ \r
+ /**\r
+ * A 16-bit unsigned integer that defines text alignment.\r
+ * This value MUST be a combination of one or more TextAlignmentMode Flags\r
+ * for text with a horizontal baseline, and VerticalTextAlignmentMode Flags\r
+ * for text with a vertical baseline.\r
+ */\r
private int textAlignmentMode;\r
\r
@Override\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
+ HwmfDrawProperties props = ctx.getProperties();\r
+ if (TA_CENTER.isSet(textAlignmentMode)) {\r
+ props.setTextAlignLatin(HwmfTextAlignment.CENTER);\r
+ } else if (TA_RIGHT.isSet(textAlignmentMode)) {\r
+ props.setTextAlignLatin(HwmfTextAlignment.RIGHT);\r
+ } else {\r
+ props.setTextAlignLatin(HwmfTextAlignment.LEFT);\r
+ }\r
+\r
+ if (VTA_CENTER.isSet(textAlignmentMode)) {\r
+ props.setTextAlignAsian(HwmfTextAlignment.CENTER);\r
+ } else if (VTA_LEFT.isSet(textAlignmentMode)) {\r
+ props.setTextAlignAsian(HwmfTextAlignment.LEFT);\r
+ } else {\r
+ props.setTextAlignAsian(HwmfTextAlignment.RIGHT);\r
+ }\r
+\r
+ if (TA_BASELINE.isSet(textAlignmentMode)) {\r
+ props.setTextVAlignLatin(HwmfTextVerticalAlignment.BASELINE);\r
+ } else if (TA_BOTTOM.isSet(textAlignmentMode)) {\r
+ props.setTextVAlignLatin(HwmfTextVerticalAlignment.BOTTOM);\r
+ } else {\r
+ props.setTextVAlignLatin(HwmfTextVerticalAlignment.TOP);\r
+ }\r
\r
+ if (VTA_BASELINE.isSet(textAlignmentMode)) {\r
+ props.setTextVAlignAsian(HwmfTextVerticalAlignment.BASELINE);\r
+ } else if (VTA_BOTTOM.isSet(textAlignmentMode)) {\r
+ props.setTextVAlignAsian(HwmfTextVerticalAlignment.BOTTOM);\r
+ } else {\r
+ props.setTextVAlignAsian(HwmfTextVerticalAlignment.TOP);\r
+ }\r
}\r
}\r
\r
\r
@Override\r
public void applyObject(HwmfGraphics ctx) {\r
-\r
+ ctx.getProperties().setFont(font);\r
}\r
}\r
}\r
\r
package org.apache.poi.hwmf.usermodel;\r
\r
+import java.awt.Dimension;\r
import java.awt.Graphics2D;\r
+import java.awt.GraphicsConfiguration;\r
import java.awt.geom.AffineTransform;\r
import java.awt.geom.Rectangle2D;\r
+import java.awt.geom.Rectangle2D.Double;\r
import java.io.BufferedInputStream;\r
import java.io.IOException;\r
import java.io.InputStream;\r
import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowExt;\r
import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowOrg;\r
import org.apache.poi.util.LittleEndianInputStream;\r
+import org.apache.poi.util.Units;\r
\r
public class HwmfPicture {\r
final List<HwmfRecord> records = new ArrayList<HwmfRecord>();\r
}\r
\r
public void draw(Graphics2D ctx) {\r
+ Dimension dim = getSize();\r
+ int width = Units.pointsToPixel(dim.getWidth());\r
+ // keep aspect ratio for height\r
+ int height = Units.pointsToPixel(dim.getHeight());\r
+ Rectangle2D bounds = new Rectangle2D.Double(0,0,width,height);\r
+ draw(ctx, bounds);\r
+ }\r
+ \r
+ public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) {\r
AffineTransform at = ctx.getTransform();\r
try {\r
- HwmfGraphics g = new HwmfGraphics(ctx, getBounds());\r
+ Rectangle2D wmfBounds = getBounds();\r
+ // scale output bounds to image bounds\r
+ ctx.scale(graphicsBounds.getWidth()/wmfBounds.getWidth(), graphicsBounds.getHeight()/wmfBounds.getHeight());\r
+ ctx.translate(-wmfBounds.getX(), -wmfBounds.getY());\r
+ \r
+ HwmfGraphics g = new HwmfGraphics(ctx, wmfBounds);\r
for (HwmfRecord r : records) {\r
r.draw(g);\r
}\r
public HwmfHeader getHeader() {\r
return header;\r
}\r
+ \r
+ /**\r
+ * Return the image size in points\r
+ *\r
+ * @return the image size in points\r
+ */\r
+ public Dimension getSize() {\r
+ double inch = (placeableHeader == null) ? 1440 : placeableHeader.getUnitsPerInch();\r
+ Rectangle2D bounds = getBounds();\r
+ \r
+ //coefficient to translate from WMF dpi to 72dpi\r
+ double coeff = Units.POINT_DPI/inch;\r
+ return new Dimension((int)Math.round(bounds.getWidth()*coeff), (int)Math.round(bounds.getHeight()*coeff));\r
+ }\r
}\r
\r
import static org.junit.Assert.assertEquals;\r
\r
+import java.awt.Dimension;\r
import java.awt.Graphics2D;\r
import java.awt.RenderingHints;\r
-import java.awt.geom.Rectangle2D;\r
import java.awt.image.BufferedImage;\r
import java.io.File;\r
import java.io.FileFilter;\r
import org.apache.poi.sl.usermodel.PictureData.PictureType;\r
import org.apache.poi.sl.usermodel.SlideShow;\r
import org.apache.poi.sl.usermodel.SlideShowFactory;\r
+import org.apache.poi.util.Units;\r
import org.junit.Ignore;\r
import org.junit.Test;\r
\r
}\r
\r
@Test\r
- @Ignore\r
+ @Ignore("This is work-in-progress and not a real unit test ...")\r
public void paint() throws IOException {\r
File f = POIDataSamples.getSlideShowInstance().getFile("santa.wmf");\r
+// File f = new File("E:\\project\\poi\\misc\\govdocs-ppt", "000133-0001.wmf");\r
FileInputStream fis = new FileInputStream(f);\r
HwmfPicture wmf = new HwmfPicture(fis);\r
fis.close();\r
\r
- Rectangle2D bounds = wmf.getBounds();\r
- int width = 300;\r
+ Dimension dim = wmf.getSize();\r
+ int width = Units.pointsToPixel(dim.getWidth());\r
// keep aspect ratio for height\r
- int height = (int)(width*bounds.getHeight()/bounds.getWidth());\r
+ int height = Units.pointsToPixel(dim.getHeight());\r
\r
BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);\r
Graphics2D g = bufImg.createGraphics();\r
}\r
\r
@Test\r
- @Ignore\r
+ @Ignore("This is work-in-progress and not a real unit test ...")\r
public void fetchWmfFromGovdocs() throws IOException {\r
URL url = new URL("http://digitalcorpora.org/corpora/files/govdocs1/by_type/ppt.zip");\r
File outdir = new File("build/ppt");\r
}\r
}\r
}\r
- \r
+\r
@Test\r
- @Ignore\r
+ @Ignore("This is work-in-progress and not a real unit test ...")\r
public void parseWmfs() throws IOException {\r
+ // parse and render the extracted wmfs from the fetchWmfFromGovdocs step\r
boolean outputFiles = false;\r
- File indir = new File("build/ppt"), outdir = indir;\r
+ boolean renderWmf = true;\r
+ File indir = new File("E:\\project\\poi\\misc\\govdocs-ppt");\r
+ File outdir = new File("build/wmf");\r
+ outdir.mkdirs();\r
final String startFile = "";\r
File files[] = indir.listFiles(new FileFilter() {\r
boolean foundStartFile = false;\r
bmpIndex++;\r
}\r
}\r
+\r
+ if (renderWmf) {\r
+ Dimension dim = wmf.getSize();\r
+ int width = Units.pointsToPixel(dim.getWidth());\r
+ // keep aspect ratio for height\r
+ int height = Units.pointsToPixel(dim.getHeight());\r
+ \r
+ BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);\r
+ Graphics2D g = bufImg.createGraphics();\r
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\r
+ g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);\r
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);\r
+ g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);\r
+ \r
+ wmf.draw(g);\r
+\r
+ g.dispose();\r
+ \r
+ ImageIO.write(bufImg, "PNG", new File(outdir, basename+".png"));\r
+ }\r
} catch (Exception e) {\r
System.out.println(f.getName()+" ignored."); \r
}\r