import java.awt.BasicStroke;\r
import java.awt.Color;\r
import java.awt.Graphics2D;\r
+import java.awt.GraphicsConfiguration;\r
import java.awt.Paint;\r
import java.awt.Rectangle;\r
import java.awt.Shape;\r
\r
import org.apache.poi.hwmf.record.HwmfBrushStyle;\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
private List<HwmfObjectTableEntry> objectTable = new ArrayList<HwmfObjectTableEntry>();\r
/** Bounding box from the placeable header */ \r
private final Rectangle2D bbox;\r
+ private final AffineTransform initialAT;\r
\r
/**\r
* Initialize a graphics context for wmf rendering\r
public HwmfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {\r
this.graphicsCtx = graphicsCtx;\r
this.bbox = (Rectangle2D)bbox.clone();\r
+ this.initialAT = graphicsCtx.getTransform();\r
}\r
\r
public HwmfDrawProperties getProperties() {\r
return;\r
}\r
\r
- Shape tshape = fitShapeToView(shape);\r
BasicStroke stroke = getStroke();\r
\r
// first draw a solid background line (depending on bkmode)\r
if (prop.getBkMode() == HwmfBkMode.OPAQUE && (lineDash != HwmfLineDash.SOLID && lineDash != HwmfLineDash.INSIDEFRAME)) {\r
graphicsCtx.setStroke(new BasicStroke(stroke.getLineWidth()));\r
graphicsCtx.setColor(prop.getBackgroundColor().getColor());\r
- graphicsCtx.draw(tshape);\r
+ graphicsCtx.draw(shape);\r
}\r
\r
// then draw the (dashed) line\r
graphicsCtx.setStroke(stroke);\r
graphicsCtx.setColor(prop.getPenColor().getColor());\r
- graphicsCtx.draw(tshape);\r
+ graphicsCtx.draw(shape);\r
}\r
\r
public void fill(Shape shape) {\r
if (prop.getBrushStyle() != HwmfBrushStyle.BS_NULL) {\r
GeneralPath gp = new GeneralPath(shape);\r
gp.setWindingRule(prop.getPolyfillMode().awtFlag);\r
- Shape tshape = fitShapeToView(gp);\r
graphicsCtx.setPaint(getFill());\r
- graphicsCtx.fill(tshape);\r
+ graphicsCtx.fill(shape);\r
}\r
\r
draw(shape);\r
}\r
\r
- protected Shape fitShapeToView(Shape shape) {\r
- int scaleUnits = prop.getMapMode().scale;\r
- Rectangle2D view = prop.getViewport();\r
- Rectangle2D win = prop.getWindow();\r
- if (view == null) {\r
- view = win;\r
- }\r
- double scaleX, scaleY;\r
- switch (scaleUnits) {\r
- case -1:\r
- scaleX = view.getWidth() / win.getWidth();\r
- scaleY = view.getHeight() / win.getHeight();\r
- break;\r
- case 0:\r
- scaleX = scaleY = 1;\r
- break;\r
- default:\r
- scaleX = scaleY = scaleUnits / (double)Units.POINT_DPI;\r
- }\r
-\r
- AffineTransform at = new AffineTransform();\r
- at.scale(scaleX, scaleY);\r
-// at.translate(-view.getX(), -view.getY());\r
- at.translate(bbox.getWidth()/win.getWidth(), bbox.getHeight()/win.getHeight());\r
-\r
- Shape tshape = at.createTransformedShape(shape);\r
- return tshape;\r
- }\r
-\r
protected BasicStroke getStroke() {\r
Rectangle2D view = prop.getViewport();\r
Rectangle2D win = prop.getWindow();\r
}\r
prop = propStack.remove(stackIndex);\r
}\r
+\r
+ public void updateWindowMapMode() {\r
+ GraphicsConfiguration gc = graphicsCtx.getDeviceConfiguration();\r
+ Rectangle2D win = prop.getWindow();\r
+ HwmfMapMode mapMode = prop.getMapMode();\r
+ graphicsCtx.setTransform(initialAT);\r
+\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
+ 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
+ break;\r
+ case MM_LOMETRIC:\r
+ case MM_HIMETRIC:\r
+ case MM_LOENGLISH:\r
+ case MM_HIENGLISH:\r
+ case MM_TWIPS:\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
+ break;\r
+ case MM_TEXT:\r
+ // TODO: to be validated ...\r
+ break;\r
+ }\r
+ }\r
}\r
\r
package org.apache.poi.hwmf.record;\r
\r
+import java.awt.Color;\r
import java.io.IOException;\r
\r
import org.apache.poi.hwmf.draw.HwmfGraphics;\r
public class HwmfPalette {\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
+\r
+ static PaletteEntryFlag valueOf(int flag) {\r
+ for (PaletteEntryFlag pef : values()) {\r
+ if (pef.flag == flag) return pef;\r
+ }\r
+ return null;\r
+ } \r
+\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 int values, blue, green, red;\r
+ private PaletteEntryFlag values;\r
+ private Color colorRef;\r
\r
public int init(LittleEndianInputStream leis) throws IOException {\r
- values = leis.readUByte();\r
- blue = leis.readUByte();\r
- green = leis.readUByte();\r
- red = leis.readUByte();\r
+ values = PaletteEntryFlag.valueOf(leis.readUByte());\r
+ int blue = leis.readUByte();\r
+ int green = leis.readUByte();\r
+ int red = leis.readUByte();\r
+ colorRef = new Color(red, green, blue);\r
+ \r
return 4*LittleEndianConsts.BYTE_SIZE;\r
}\r
}\r
*/\r
private int numberOfEntries;\r
\r
- PaletteEntry entries[];\r
+ private PaletteEntry entries[];\r
\r
@Override\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r