WMF fixes

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1723198 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2016-01-05 23:40:47 +00:00
parent 0190e79270
commit 799eced5bb
7 changed files with 112 additions and 47 deletions

View File

@ -20,6 +20,7 @@ package org.apache.poi.hwmf.draw;
import java.awt.BasicStroke; import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Paint; import java.awt.Paint;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Shape; import java.awt.Shape;
@ -36,6 +37,7 @@ import java.util.NoSuchElementException;
import org.apache.poi.hwmf.record.HwmfBrushStyle; import org.apache.poi.hwmf.record.HwmfBrushStyle;
import org.apache.poi.hwmf.record.HwmfHatchStyle; 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.HwmfMisc.WmfSetBkMode.HwmfBkMode;
import org.apache.poi.hwmf.record.HwmfObjectTableEntry; import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
import org.apache.poi.hwmf.record.HwmfPenStyle; import org.apache.poi.hwmf.record.HwmfPenStyle;
@ -49,6 +51,7 @@ public class HwmfGraphics {
private List<HwmfObjectTableEntry> objectTable = new ArrayList<HwmfObjectTableEntry>(); private List<HwmfObjectTableEntry> objectTable = new ArrayList<HwmfObjectTableEntry>();
/** Bounding box from the placeable header */ /** Bounding box from the placeable header */
private final Rectangle2D bbox; private final Rectangle2D bbox;
private final AffineTransform initialAT;
/** /**
* Initialize a graphics context for wmf rendering * Initialize a graphics context for wmf rendering
@ -59,6 +62,7 @@ public class HwmfGraphics {
public HwmfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) { public HwmfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {
this.graphicsCtx = graphicsCtx; this.graphicsCtx = graphicsCtx;
this.bbox = (Rectangle2D)bbox.clone(); this.bbox = (Rectangle2D)bbox.clone();
this.initialAT = graphicsCtx.getTransform();
} }
public HwmfDrawProperties getProperties() { public HwmfDrawProperties getProperties() {
@ -72,7 +76,6 @@ public class HwmfGraphics {
return; return;
} }
Shape tshape = fitShapeToView(shape);
BasicStroke stroke = getStroke(); BasicStroke stroke = getStroke();
// first draw a solid background line (depending on bkmode) // first draw a solid background line (depending on bkmode)
@ -80,56 +83,26 @@ public class HwmfGraphics {
if (prop.getBkMode() == HwmfBkMode.OPAQUE && (lineDash != HwmfLineDash.SOLID && lineDash != HwmfLineDash.INSIDEFRAME)) { if (prop.getBkMode() == HwmfBkMode.OPAQUE && (lineDash != HwmfLineDash.SOLID && lineDash != HwmfLineDash.INSIDEFRAME)) {
graphicsCtx.setStroke(new BasicStroke(stroke.getLineWidth())); graphicsCtx.setStroke(new BasicStroke(stroke.getLineWidth()));
graphicsCtx.setColor(prop.getBackgroundColor().getColor()); graphicsCtx.setColor(prop.getBackgroundColor().getColor());
graphicsCtx.draw(tshape); graphicsCtx.draw(shape);
} }
// then draw the (dashed) line // then draw the (dashed) line
graphicsCtx.setStroke(stroke); graphicsCtx.setStroke(stroke);
graphicsCtx.setColor(prop.getPenColor().getColor()); graphicsCtx.setColor(prop.getPenColor().getColor());
graphicsCtx.draw(tshape); graphicsCtx.draw(shape);
} }
public void fill(Shape shape) { public void fill(Shape shape) {
if (prop.getBrushStyle() != HwmfBrushStyle.BS_NULL) { if (prop.getBrushStyle() != HwmfBrushStyle.BS_NULL) {
GeneralPath gp = new GeneralPath(shape); GeneralPath gp = new GeneralPath(shape);
gp.setWindingRule(prop.getPolyfillMode().awtFlag); gp.setWindingRule(prop.getPolyfillMode().awtFlag);
Shape tshape = fitShapeToView(gp);
graphicsCtx.setPaint(getFill()); graphicsCtx.setPaint(getFill());
graphicsCtx.fill(tshape); graphicsCtx.fill(shape);
} }
draw(shape); draw(shape);
} }
protected Shape fitShapeToView(Shape shape) {
int scaleUnits = prop.getMapMode().scale;
Rectangle2D view = prop.getViewport();
Rectangle2D win = prop.getWindow();
if (view == null) {
view = win;
}
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.scale(scaleX, scaleY);
// at.translate(-view.getX(), -view.getY());
at.translate(bbox.getWidth()/win.getWidth(), bbox.getHeight()/win.getHeight());
Shape tshape = at.createTransformedShape(shape);
return tshape;
}
protected BasicStroke getStroke() { protected BasicStroke getStroke() {
Rectangle2D view = prop.getViewport(); Rectangle2D view = prop.getViewport();
Rectangle2D win = prop.getWindow(); Rectangle2D win = prop.getWindow();
@ -285,4 +258,47 @@ public class HwmfGraphics {
} }
prop = propStack.remove(stackIndex); prop = propStack.remove(stackIndex);
} }
public void updateWindowMapMode() {
GraphicsConfiguration gc = graphicsCtx.getDeviceConfiguration();
Rectangle2D win = prop.getWindow();
HwmfMapMode mapMode = prop.getMapMode();
graphicsCtx.setTransform(initialAT);
switch (mapMode) {
default:
case MM_ANISOTROPIC:
// scale output bounds to image bounds
graphicsCtx.scale(gc.getBounds().getWidth()/bbox.getWidth(), gc.getBounds().getHeight()/bbox.getHeight());
graphicsCtx.translate(-bbox.getX(), -bbox.getY());
// scale window bounds to output bounds
graphicsCtx.translate(win.getCenterX(), win.getCenterY());
graphicsCtx.scale(bbox.getWidth()/win.getWidth(), bbox.getHeight()/win.getHeight());
graphicsCtx.translate(-win.getCenterX(), -win.getCenterY());
break;
case MM_ISOTROPIC:
// TODO: to be validated ...
// like anisotropic, but use x-axis as reference
graphicsCtx.scale(gc.getBounds().getWidth()/bbox.getWidth(), gc.getBounds().getWidth()/bbox.getWidth());
graphicsCtx.translate(-bbox.getX(), -bbox.getY());
graphicsCtx.translate(win.getCenterX(), win.getCenterY());
graphicsCtx.scale(bbox.getWidth()/win.getWidth(), bbox.getWidth()/win.getWidth());
graphicsCtx.translate(-win.getCenterX(), -win.getCenterY());
break;
case MM_LOMETRIC:
case MM_HIMETRIC:
case MM_LOENGLISH:
case MM_HIENGLISH:
case MM_TWIPS:
// TODO: to be validated ...
graphicsCtx.transform(gc.getNormalizingTransform());
graphicsCtx.scale(1./mapMode.scale, -1./mapMode.scale);
graphicsCtx.translate(-bbox.getX(), -bbox.getY());
break;
case MM_TEXT:
// TODO: to be validated ...
break;
}
}
} }

View File

@ -664,7 +664,7 @@ public class HwmfDraw {
@Override @Override
public void draw(HwmfGraphics ctx) { public void draw(HwmfGraphics ctx) {
ctx.applyObjectTableEntry(objectIndex);
} }
} }
} }

View File

@ -221,6 +221,7 @@ public class HwmfMisc {
@Override @Override
public void draw(HwmfGraphics ctx) { public void draw(HwmfGraphics ctx) {
ctx.getProperties().setMapMode(mapMode); ctx.getProperties().setMapMode(mapMode);
ctx.updateWindowMapMode();
} }
} }

View File

@ -17,6 +17,7 @@
package org.apache.poi.hwmf.record; package org.apache.poi.hwmf.record;
import java.awt.Color;
import java.io.IOException; import java.io.IOException;
import org.apache.poi.hwmf.draw.HwmfGraphics; import org.apache.poi.hwmf.draw.HwmfGraphics;
@ -26,18 +27,58 @@ import org.apache.poi.util.LittleEndianInputStream;
public class HwmfPalette { public class HwmfPalette {
public static class PaletteEntry { public static class PaletteEntry {
enum PaletteEntryFlag {
/**
* Specifies that the logical palette entry be used for palette animation. This value
* prevents other windows from matching colors to the palette entry because the color frequently
* changes. If an unused system-palette entry is available, the color is placed in that entry.
* Otherwise, the color is not available for animation.
*/
PC_RESERVED(0x01),
/**
* Specifies that the low-order word of the logical palette entry designates a hardware
* palette index. This value allows the application to show the contents of the display device palette.
*/
PC_EXPLICIT(0x02),
/**
* Specifies that the color be placed in an unused entry in the system palette
* instead of being matched to an existing color in the system palette. If there are no unused entries
* in the system palette, the color is matched normally. Once this color is in the system palette,
* colors in other logical palettes can be matched to this color.
*/
PC_NOCOLLAPSE(0x04)
;
int flag;
PaletteEntryFlag(int flag) {
this.flag = flag;
}
static PaletteEntryFlag valueOf(int flag) {
for (PaletteEntryFlag pef : values()) {
if (pef.flag == flag) return pef;
}
return null;
}
}
// Values (1 byte): An 8-bit unsigned integer that defines how the palette entry is to be used. // Values (1 byte): An 8-bit unsigned integer that defines how the palette entry is to be used.
// The Values field MUST be 0x00 or one of the values in the PaletteEntryFlag Enumeration table. // The Values field MUST be 0x00 or one of the values in the PaletteEntryFlag Enumeration table.
// Blue (1 byte): An 8-bit unsigned integer that defines the blue intensity value for the palette entry. // 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. // 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. // Red (1 byte): An 8-bit unsigned integer that defines the red intensity value for the palette entry.
private int values, blue, green, red; private PaletteEntryFlag values;
private Color colorRef;
public int init(LittleEndianInputStream leis) throws IOException { public int init(LittleEndianInputStream leis) throws IOException {
values = leis.readUByte(); values = PaletteEntryFlag.valueOf(leis.readUByte());
blue = leis.readUByte(); int blue = leis.readUByte();
green = leis.readUByte(); int green = leis.readUByte();
red = leis.readUByte(); int red = leis.readUByte();
colorRef = new Color(red, green, blue);
return 4*LittleEndianConsts.BYTE_SIZE; return 4*LittleEndianConsts.BYTE_SIZE;
} }
} }
@ -57,7 +98,7 @@ public class HwmfPalette {
*/ */
private int numberOfEntries; private int numberOfEntries;
PaletteEntry entries[]; private PaletteEntry entries[];
@Override @Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {

View File

@ -167,6 +167,7 @@ public class HwmfWindowing {
@Override @Override
public void draw(HwmfGraphics ctx) { public void draw(HwmfGraphics ctx) {
ctx.getProperties().setWindowOrg(x, y); ctx.getProperties().setWindowOrg(x, y);
ctx.updateWindowMapMode();
} }
public int getY() { public int getY() {
@ -211,6 +212,7 @@ public class HwmfWindowing {
@Override @Override
public void draw(HwmfGraphics ctx) { public void draw(HwmfGraphics ctx) {
ctx.getProperties().setWindowExt(width, height); ctx.getProperties().setWindowExt(width, height);
ctx.updateWindowMapMode();
} }
public int getHeight() { public int getHeight() {
@ -254,6 +256,7 @@ public class HwmfWindowing {
public void draw(HwmfGraphics ctx) { public void draw(HwmfGraphics ctx) {
Rectangle2D window = ctx.getProperties().getWindow(); Rectangle2D window = ctx.getProperties().getWindow();
ctx.getProperties().setWindowOrg(window.getX()+xOffset, window.getY()+yOffset); ctx.getProperties().setWindowOrg(window.getX()+xOffset, window.getY()+yOffset);
ctx.updateWindowMapMode();
} }
} }
@ -307,6 +310,7 @@ public class HwmfWindowing {
double width = window.getWidth() * xNum / xDenom; double width = window.getWidth() * xNum / xDenom;
double height = window.getHeight() * yNum / yDenom; double height = window.getHeight() * yNum / yDenom;
ctx.getProperties().setWindowExt(width, height); ctx.getProperties().setWindowExt(width, height);
ctx.updateWindowMapMode();
} }
} }

View File

@ -18,6 +18,7 @@
package org.apache.poi.hwmf.usermodel; package org.apache.poi.hwmf.usermodel;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.IOException; import java.io.IOException;
@ -84,9 +85,14 @@ public class HwmfPicture {
} }
public void draw(Graphics2D ctx) { public void draw(Graphics2D ctx) {
HwmfGraphics g = new HwmfGraphics(ctx, getBounds()); AffineTransform at = ctx.getTransform();
for (HwmfRecord r : records) { try {
r.draw(g); HwmfGraphics g = new HwmfGraphics(ctx, getBounds());
for (HwmfRecord r : records) {
r.draw(g);
}
} finally {
ctx.setTransform(at);
} }
} }

View File

@ -79,9 +79,6 @@ public class TestHwmfParsing {
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g.scale(width/bounds.getWidth(), height/bounds.getHeight());
g.translate(-bounds.getX(), -bounds.getY());
wmf.draw(g); wmf.draw(g);
g.dispose(); g.dispose();