git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1722771 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_14_FINAL
@@ -18,9 +18,13 @@ | |||
package org.apache.poi.hwmf.draw; | |||
import java.awt.Color; | |||
import java.awt.Shape; | |||
import java.awt.geom.Area; | |||
import java.awt.geom.Point2D; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.image.BufferedImage; | |||
import java.awt.image.ColorModel; | |||
import java.awt.image.WritableRaster; | |||
import org.apache.poi.hwmf.record.HwmfBrushStyle; | |||
import org.apache.poi.hwmf.record.HwmfColorRef; | |||
@@ -31,9 +35,9 @@ 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 final Rectangle2D window; | |||
private Rectangle2D viewport = null; | |||
private final Point2D location; | |||
private HwmfMapMode mapMode = HwmfMapMode.MM_ANISOTROPIC; | |||
private HwmfColorRef backgroundColor = new HwmfColorRef(Color.BLACK); | |||
private HwmfBrushStyle brushStyle = HwmfBrushStyle.BS_SOLID; | |||
@@ -46,7 +50,42 @@ public class HwmfDrawProperties { | |||
private double penMiterLimit = 10; | |||
private HwmfBkMode bkMode = HwmfBkMode.OPAQUE; | |||
private HwmfPolyfillMode polyfillMode = HwmfPolyfillMode.WINDING; | |||
private Shape region = null; | |||
public HwmfDrawProperties() { | |||
window = new Rectangle2D.Double(0, 0, 1, 1); | |||
viewport = null; | |||
location = new Point2D.Double(0,0); | |||
} | |||
public HwmfDrawProperties(HwmfDrawProperties other) { | |||
this.window = (Rectangle2D)other.window.clone(); | |||
this.viewport = (other.viewport == null) ? null : (Rectangle2D)other.viewport.clone(); | |||
this.location = (Point2D)other.location.clone(); | |||
this.mapMode = other.mapMode; | |||
this.backgroundColor = (other.backgroundColor == null) ? null : other.backgroundColor.clone(); | |||
this.brushStyle = other.brushStyle; | |||
this.brushColor = other.brushColor.clone(); | |||
this.brushHatch = other.brushHatch; | |||
if (other.brushBitmap != null) { | |||
ColorModel cm = other.brushBitmap.getColorModel(); | |||
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); | |||
WritableRaster raster = other.brushBitmap.copyData(null); | |||
this.brushBitmap = new BufferedImage(cm, raster, isAlphaPremultiplied, null); | |||
} | |||
this.penWidth = 1; | |||
this.penStyle = (other.penStyle == null) ? null : other.penStyle.clone(); | |||
this.penColor = (other.penColor == null) ? null : other.penColor.clone(); | |||
this.penMiterLimit = other.penMiterLimit; | |||
this.bkMode = other.bkMode; | |||
this.polyfillMode = other.polyfillMode; | |||
if (other.region instanceof Rectangle2D) { | |||
this.region = ((Rectangle2D)other.region).getBounds2D(); | |||
} else if (other.region instanceof Area) { | |||
this.region = new Area(other.region); | |||
} | |||
} | |||
public void setViewportExt(double width, double height) { | |||
double x = viewport.getX(); | |||
double y = viewport.getY(); | |||
@@ -62,7 +101,7 @@ public class HwmfDrawProperties { | |||
} | |||
public Rectangle2D getViewport() { | |||
return (Rectangle2D)viewport.clone(); | |||
return (viewport == null) ? null : (Rectangle2D)viewport.clone(); | |||
} | |||
public void setWindowExt(double width, double height) { | |||
@@ -186,4 +225,23 @@ public class HwmfDrawProperties { | |||
public void setBrushBitmap(BufferedImage brushBitmap) { | |||
this.brushBitmap = brushBitmap; | |||
} | |||
/** | |||
* Gets the last stored region | |||
* | |||
* @return the last stored region | |||
*/ | |||
public Shape getRegion() { | |||
return region; | |||
} | |||
/** | |||
* Sets a region for further usage | |||
* | |||
* @param region a region object which is usually a rectangle | |||
*/ | |||
public void setRegion(Shape region) { | |||
this.region = region; | |||
} | |||
} |
@@ -28,25 +28,37 @@ 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 java.util.ArrayList; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.ListIterator; | |||
import java.util.NoSuchElementException; | |||
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.HwmfObjectTableEntry; | |||
import org.apache.poi.hwmf.record.HwmfPenStyle; | |||
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash; | |||
import org.apache.poi.util.Units; | |||
public class HwmfGraphics { | |||
private final Graphics2D graphicsCtx; | |||
private final Deque<HwmfDrawProperties> propStack = new ArrayDeque<HwmfDrawProperties>(); | |||
HwmfDrawProperties prop; | |||
private final List<HwmfDrawProperties> propStack = new LinkedList<HwmfDrawProperties>(); | |||
private HwmfDrawProperties prop = new HwmfDrawProperties(); | |||
private List<HwmfObjectTableEntry> objectTable = new ArrayList<HwmfObjectTableEntry>(); | |||
/** Bounding box from the placeable header */ | |||
private final Rectangle2D bbox; | |||
public HwmfGraphics(Graphics2D graphicsCtx) { | |||
/** | |||
* Initialize a graphics context for wmf rendering | |||
* | |||
* @param graphicsCtx the graphics context to delegate drawing calls | |||
* @param bbox the bounding box of the wmf (taken from the placeable header) | |||
*/ | |||
public HwmfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) { | |||
this.graphicsCtx = graphicsCtx; | |||
prop = new HwmfDrawProperties(); | |||
propStack.push(prop); | |||
this.bbox = (Rectangle2D)bbox.clone(); | |||
} | |||
public HwmfDrawProperties getProperties() { | |||
@@ -91,7 +103,11 @@ public class HwmfGraphics { | |||
protected Shape fitShapeToView(Shape shape) { | |||
int scaleUnits = prop.getMapMode().scale; | |||
Rectangle2D view = prop.getViewport(), win = prop.getWindow(); | |||
Rectangle2D view = prop.getViewport(); | |||
Rectangle2D win = prop.getWindow(); | |||
if (view == null) { | |||
view = win; | |||
} | |||
double scaleX, scaleY; | |||
switch (scaleUnits) { | |||
case -1: | |||
@@ -106,16 +122,20 @@ public class HwmfGraphics { | |||
} | |||
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()); | |||
// at.translate(-view.getX(), -view.getY()); | |||
at.translate(bbox.getWidth()/win.getWidth(), bbox.getHeight()/win.getHeight()); | |||
return at.createTransformedShape(shape); | |||
Shape tshape = at.createTransformedShape(shape); | |||
return tshape; | |||
} | |||
protected BasicStroke getStroke() { | |||
Rectangle2D view = prop.getViewport(), win = prop.getWindow(); | |||
Rectangle2D view = prop.getViewport(); | |||
Rectangle2D win = prop.getWindow(); | |||
if (view == null) { | |||
view = win; | |||
} | |||
float width = (float)(prop.getPenWidth() * view.getWidth() / win.getWidth()); | |||
HwmfPenStyle ps = prop.getPenStyle(); | |||
int cap = ps.getLineCap().awtFlag; | |||
@@ -182,4 +202,87 @@ public class HwmfGraphics { | |||
return (bi == null) ? null | |||
: new TexturePaint(bi, new Rectangle(0,0,bi.getWidth(),bi.getHeight())); | |||
} | |||
/** | |||
* Adds an record of type {@link HwmfObjectTableEntry} to the object table. | |||
* | |||
* Every object is assigned the lowest available index-that is, the smallest | |||
* numerical value-in the WMF Object Table. This binding happens at object creation, | |||
* not when the object is used. | |||
* Moreover, each object table index uniquely refers to an object. | |||
* Indexes in the WMF Object Table always start at 0. | |||
* | |||
* @param entry | |||
*/ | |||
public void addObjectTableEntry(HwmfObjectTableEntry entry) { | |||
ListIterator<HwmfObjectTableEntry> oIter = objectTable.listIterator(); | |||
while (oIter.hasNext()) { | |||
HwmfObjectTableEntry tableEntry = oIter.next(); | |||
if (tableEntry == null) { | |||
oIter.set(entry); | |||
return; | |||
} | |||
} | |||
objectTable.add(entry); | |||
} | |||
/** | |||
* Applies the object table entry | |||
* | |||
* @param index the index of the object table entry (0-based) | |||
* | |||
* @throws IndexOutOfBoundsException if the index is out of range | |||
* @throws NoSuchElementException if the entry was deleted before | |||
*/ | |||
public void applyObjectTableEntry(int index) { | |||
HwmfObjectTableEntry ote = objectTable.get(index); | |||
if (ote == null) { | |||
throw new NoSuchElementException("WMF reference exception - object table entry on index "+index+" was deleted before."); | |||
} | |||
ote.applyObject(this); | |||
} | |||
/** | |||
* Unsets (deletes) the object table entry for further usage | |||
* | |||
* When a META_DELETEOBJECT record (section 2.3.4.7) is received that specifies this | |||
* object's particular index, the object's resources are released, the binding to its | |||
* WMF Object Table index is ended, and the index value is returned to the pool of | |||
* available indexes. The index will be reused, if needed, by a subsequent object | |||
* created by another Object Record Type record. | |||
* | |||
* @param index the index (0-based) | |||
* | |||
* @throws IndexOutOfBoundsException if the index is out of range | |||
*/ | |||
public void unsetObjectTableEntry(int index) { | |||
objectTable.set(index, null); | |||
} | |||
/** | |||
* Saves the current properties to the stack | |||
*/ | |||
public void saveProperties() { | |||
propStack.add(prop); | |||
prop = new HwmfDrawProperties(prop); | |||
} | |||
/** | |||
* Restores the properties from the stack | |||
* | |||
* @param index if the index is positive, the n-th element from the start is removed and activated. | |||
* If the index is negative, the n-th previous element relative to the current properties element is removed and activated. | |||
*/ | |||
public void restoreProperties(int index) { | |||
if (index == 0) { | |||
return; | |||
} | |||
int stackIndex = index; | |||
if (stackIndex < 0) { | |||
int curIdx = propStack.indexOf(prop); | |||
assert (curIdx != -1); | |||
stackIndex = curIdx + index; | |||
} | |||
prop = propStack.remove(stackIndex); | |||
} | |||
} |
@@ -30,7 +30,7 @@ import org.apache.poi.util.LittleEndianInputStream; | |||
* 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 { | |||
public class HwmfColorRef implements Cloneable { | |||
private Color colorRef = Color.BLACK; | |||
public HwmfColorRef() {} | |||
@@ -53,4 +53,21 @@ public class HwmfColorRef { | |||
public Color getColor() { | |||
return colorRef; | |||
} | |||
/** | |||
* Creates a new object of the same class and with the | |||
* same contents as this object. | |||
* @return a clone of this instance. | |||
* @exception OutOfMemoryError if there is not enough memory. | |||
* @see java.lang.Cloneable | |||
*/ | |||
@Override | |||
public HwmfColorRef clone() { | |||
try { | |||
return (HwmfColorRef)super.clone(); | |||
} catch (CloneNotSupportedException e) { | |||
// this shouldn't happen, since we are Cloneable | |||
throw new InternalError(); | |||
} | |||
} | |||
} |
@@ -18,9 +18,15 @@ | |||
package org.apache.poi.hwmf.record; | |||
import java.awt.Polygon; | |||
import java.awt.Rectangle; | |||
import java.awt.Shape; | |||
import java.awt.geom.Arc2D; | |||
import java.awt.geom.Ellipse2D; | |||
import java.awt.geom.GeneralPath; | |||
import java.awt.geom.Line2D; | |||
import java.awt.geom.Point2D; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.geom.RoundRectangle2D; | |||
import java.io.IOException; | |||
import org.apache.poi.hwmf.draw.HwmfGraphics; | |||
@@ -55,7 +61,7 @@ public class HwmfDraw { | |||
x = leis.readShort(); | |||
return 2*LittleEndianConsts.SHORT_SIZE; | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.getProperties().setLocation(x, y); | |||
@@ -91,7 +97,7 @@ public class HwmfDraw { | |||
x = leis.readShort(); | |||
return 2*LittleEndianConsts.SHORT_SIZE; | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
Point2D start = ctx.getProperties().getLocation(); | |||
@@ -135,12 +141,12 @@ public class HwmfDraw { | |||
return LittleEndianConsts.SHORT_SIZE+numberofPoints*LittleEndianConsts.INT_SIZE; | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.fill(getShape()); | |||
} | |||
protected Polygon getShape() { | |||
Polygon polygon = new Polygon(); | |||
for(int i = 0; i < numberofPoints; i++) { | |||
@@ -207,10 +213,15 @@ public class HwmfDraw { | |||
leftRect = leis.readShort(); | |||
return 4*LittleEndianConsts.SHORT_SIZE; | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
int x = Math.min(leftRect, rightRect); | |||
int y = Math.min(topRect, bottomRect); | |||
int w = Math.abs(leftRect - rightRect - 1); | |||
int h = Math.abs(topRect - bottomRect - 1); | |||
Shape s = new Ellipse2D.Double(x, y, w, h); | |||
ctx.fill(s); | |||
} | |||
} | |||
@@ -253,10 +264,10 @@ public class HwmfDraw { | |||
width = leis.readShort(); | |||
return 4*LittleEndianConsts.SHORT_SIZE; | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
} | |||
} | |||
@@ -372,7 +383,12 @@ public class HwmfDraw { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
int x = Math.min(leftRect, rightRect); | |||
int y = Math.min(topRect, bottomRect); | |||
int w = Math.abs(leftRect - rightRect - 1); | |||
int h = Math.abs(topRect - bottomRect - 1); | |||
Shape s = new Rectangle2D.Double(x, y, w, h); | |||
ctx.fill(s); | |||
} | |||
} | |||
@@ -415,7 +431,8 @@ public class HwmfDraw { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
Shape s = new Rectangle2D.Double(x, y, 1, 1); | |||
ctx.fill(s); | |||
} | |||
} | |||
@@ -479,84 +496,16 @@ public class HwmfDraw { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
int x = Math.min(leftRect, rightRect); | |||
int y = Math.min(topRect, bottomRect); | |||
int w = Math.abs(leftRect - rightRect - 1); | |||
int h = Math.abs(topRect - bottomRect - 1); | |||
Shape s = new RoundRectangle2D.Double(x, y, w, h, width, height); | |||
ctx.fill(s); | |||
} | |||
} | |||
/** | |||
* The META_PIE record draws a pie-shaped wedge bounded by the intersection of an ellipse and two | |||
* radials. The pie is outlined by using the pen and filled by using the brush that are defined in the | |||
* playback device context. | |||
*/ | |||
public static class WmfPie implements HwmfRecord { | |||
/** | |||
* A 16-bit signed integer that defines the y-coordinate, in logical | |||
* coordinates, of the endpoint of the second radial. | |||
*/ | |||
private int yRadial2; | |||
/** | |||
* A 16-bit signed integer that defines the x-coordinate, in logical | |||
* coordinates, of the endpoint of the second radial. | |||
*/ | |||
private int xRadial2; | |||
/** | |||
* A 16-bit signed integer that defines the y-coordinate, in logical | |||
* coordinates, of the endpoint of the first radial. | |||
*/ | |||
private int yRadial1; | |||
/** | |||
* A 16-bit signed integer that defines the x-coordinate, in logical | |||
* coordinates, of the endpoint of the first radial. | |||
*/ | |||
private int xRadial1; | |||
/** | |||
* A 16-bit signed integer that defines the y-coordinate, in logical units, of | |||
* the lower-right corner of the bounding rectangle. | |||
*/ | |||
private int bottomRect; | |||
/** | |||
* A 16-bit signed integer that defines the x-coordinate, in logical units, of | |||
* the lower-right corner of the bounding rectangle. | |||
*/ | |||
private int rightRect; | |||
/** | |||
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the | |||
* upper-left corner of the bounding rectangle. | |||
*/ | |||
private int topRect; | |||
/** | |||
* A 16-bit signed integer that defines the x-coordinate, in logical units, of | |||
* the upper-left corner of the bounding rectangle. | |||
*/ | |||
private int leftRect; | |||
@Override | |||
public HwmfRecordType getRecordType() { | |||
return HwmfRecordType.pie; | |||
} | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
yRadial2 = leis.readShort(); | |||
xRadial2 = leis.readShort(); | |||
yRadial1 = leis.readShort(); | |||
xRadial1 = leis.readShort(); | |||
bottomRect = leis.readShort(); | |||
rightRect = leis.readShort(); | |||
topRect = leis.readShort(); | |||
leftRect = leis.readShort(); | |||
return 8*LittleEndianConsts.SHORT_SIZE; | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
} | |||
} | |||
/** | |||
* The META_ARC record draws an elliptical arc. | |||
*/ | |||
@@ -622,81 +571,69 @@ public class HwmfDraw { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
int x = Math.min(leftRect, rightRect); | |||
int y = Math.min(topRect, bottomRect); | |||
int w = Math.abs(leftRect - rightRect - 1); | |||
int h = Math.abs(topRect - bottomRect - 1); | |||
double startAngle = Math.toDegrees(Math.atan2(-(yStartArc - (topRect + h / 2.)), xStartArc - (leftRect + w / 2.))); | |||
double endAngle = Math.toDegrees(Math.atan2(-(yEndArc - (topRect + h / 2.)), xEndArc - (leftRect + w / 2.))); | |||
double arcAngle = (endAngle - startAngle) + (endAngle - startAngle > 0 ? 0 : 360); | |||
if (startAngle < 0) { | |||
startAngle += 360; | |||
} | |||
boolean fillShape; | |||
int arcClosure; | |||
switch (getRecordType()) { | |||
default: | |||
case arc: | |||
arcClosure = Arc2D.OPEN; | |||
fillShape = false; | |||
break; | |||
case chord: | |||
arcClosure = Arc2D.CHORD; | |||
fillShape = true; | |||
break; | |||
case pie: | |||
arcClosure = Arc2D.PIE; | |||
fillShape = true; | |||
break; | |||
} | |||
Shape s = new Arc2D.Double(x, y, w, h, startAngle, arcAngle, arcClosure); | |||
if (fillShape) { | |||
ctx.fill(s); | |||
} else { | |||
ctx.draw(s); | |||
} | |||
} | |||
} | |||
/** | |||
* The META_CHORD record draws a chord, which is defined by a region bounded by the intersection of | |||
* an ellipse with a line segment. The chord is outlined using the pen and filled using the brush | |||
* that are defined in the playback device context. | |||
* The META_PIE record draws a pie-shaped wedge bounded by the intersection of an ellipse and two | |||
* radials. The pie is outlined by using the pen and filled by using the brush that are defined in the | |||
* playback device context. | |||
*/ | |||
public static class WmfChord implements HwmfRecord { | |||
/** | |||
* A 16-bit signed integer that defines the y-coordinate, in logical | |||
* coordinates, of the endpoint of the second radial. | |||
*/ | |||
private int yRadial2; | |||
/** | |||
* A 16-bit signed integer that defines the x-coordinate, in logical | |||
* coordinates, of the endpoint of the second radial. | |||
*/ | |||
private int xRadial2; | |||
/** | |||
* A 16-bit signed integer that defines the y-coordinate, in logical | |||
* coordinates, of the endpoint of the first radial. | |||
*/ | |||
private int yRadial1; | |||
/** | |||
* A 16-bit signed integer that defines the x-coordinate, in logical | |||
* coordinates, of the endpoint of the first radial. | |||
*/ | |||
private int xRadial1; | |||
/** | |||
* A 16-bit signed integer that defines the y-coordinate, in logical units, of | |||
* the lower-right corner of the bounding rectangle. | |||
*/ | |||
private int bottomRect; | |||
/** | |||
* A 16-bit signed integer that defines the x-coordinate, in logical units, of | |||
* the lower-right corner of the bounding rectangle. | |||
*/ | |||
private int rightRect; | |||
/** | |||
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the | |||
* upper-left corner of the bounding rectangle. | |||
*/ | |||
private int topRect; | |||
/** | |||
* A 16-bit signed integer that defines the x-coordinate, in logical units, of | |||
* the upper-left corner of the bounding rectangle. | |||
*/ | |||
private int leftRect; | |||
public static class WmfPie extends WmfArc { | |||
@Override | |||
public HwmfRecordType getRecordType() { | |||
return HwmfRecordType.chord; | |||
return HwmfRecordType.pie; | |||
} | |||
} | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
yRadial2 = leis.readShort(); | |||
xRadial2 = leis.readShort(); | |||
yRadial1 = leis.readShort(); | |||
xRadial1 = leis.readShort(); | |||
bottomRect = leis.readShort(); | |||
rightRect = leis.readShort(); | |||
topRect = leis.readShort(); | |||
leftRect = leis.readShort(); | |||
return 8*LittleEndianConsts.SHORT_SIZE; | |||
} | |||
/** | |||
* The META_CHORD record draws a chord, which is defined by a region bounded by the intersection of | |||
* an ellipse with a line segment. The chord is outlined using the pen and filled using the brush | |||
* that are defined in the playback device context. | |||
*/ | |||
public static class WmfChord extends WmfArc { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
public HwmfRecordType getRecordType() { | |||
return HwmfRecordType.chord; | |||
} | |||
} | |||
} | |||
/** |
@@ -17,6 +17,7 @@ | |||
package org.apache.poi.hwmf.record; | |||
import java.awt.Shape; | |||
import java.awt.geom.Path2D; | |||
import java.awt.image.BufferedImage; | |||
import java.io.File; | |||
@@ -25,6 +26,7 @@ import java.io.IOException; | |||
import javax.imageio.ImageIO; | |||
import org.apache.poi.hwmf.draw.HwmfGraphics; | |||
import org.apache.poi.hwmf.record.HwmfWindowing.WmfCreateRegion; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
import org.apache.poi.util.LittleEndianInputStream; | |||
@@ -46,13 +48,13 @@ public class HwmfFill { | |||
* A 16-bit unsigned integer used to index into the WMF Object Table to get | |||
* the region to be filled. | |||
*/ | |||
private int region; | |||
private int regionIndex; | |||
/** | |||
* A 16-bit unsigned integer used to index into the WMF Object Table to get the | |||
* brush to use for filling the region. | |||
*/ | |||
private int brush; | |||
private int brushIndex; | |||
@Override | |||
public HwmfRecordType getRecordType() { | |||
@@ -61,13 +63,20 @@ public class HwmfFill { | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
region = leis.readUShort(); | |||
brush = leis.readUShort(); | |||
regionIndex = leis.readUShort(); | |||
brushIndex = leis.readUShort(); | |||
return 2*LittleEndianConsts.SHORT_SIZE; | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.applyObjectTableEntry(regionIndex); | |||
ctx.applyObjectTableEntry(brushIndex); | |||
Shape region = ctx.getProperties().getRegion(); | |||
if (region != null) { | |||
ctx.fill(region); | |||
} | |||
} | |||
} | |||
@@ -82,20 +91,25 @@ public class HwmfFill { | |||
* A 16-bit unsigned integer used to index into the WMF Object Table to get | |||
* the region to be painted. | |||
*/ | |||
int region; | |||
int regionIndex; | |||
public HwmfRecordType getRecordType() { | |||
return HwmfRecordType.paintRegion; | |||
} | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
region = leis.readUShort(); | |||
regionIndex = leis.readUShort(); | |||
return LittleEndianConsts.SHORT_SIZE; | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.applyObjectTableEntry(regionIndex); | |||
Shape region = ctx.getProperties().getRegion(); | |||
if (region != null) { | |||
ctx.fill(region); | |||
} | |||
} | |||
} | |||
@@ -44,7 +44,7 @@ public class HwmfMisc { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.saveProperties(); | |||
} | |||
} | |||
@@ -92,7 +92,7 @@ public class HwmfMisc { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.restoreProperties(nSavedDC); | |||
} | |||
} | |||
@@ -431,11 +431,11 @@ public class HwmfMisc { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.unsetObjectTableEntry(objectIndex); | |||
} | |||
} | |||
public static class WmfCreatePatternBrush implements HwmfRecord { | |||
public static class WmfCreatePatternBrush implements HwmfRecord, HwmfObjectTableEntry { | |||
private HwmfBitmap16 pattern; | |||
@@ -452,16 +452,23 @@ public class HwmfMisc { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.addObjectTableEntry(this); | |||
} | |||
@Override | |||
public void applyObject(HwmfGraphics ctx) { | |||
HwmfDrawProperties dp = ctx.getProperties(); | |||
dp.setBrushBitmap(pattern.getImage()); | |||
dp.setBrushStyle(HwmfBrushStyle.BS_PATTERN); | |||
} | |||
} | |||
public static class WmfCreatePenIndirect implements HwmfRecord { | |||
public static class WmfCreatePenIndirect implements HwmfRecord, HwmfObjectTableEntry { | |||
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. | |||
* The x-coordinate is the pen width. The y-coordinate is ignored. | |||
*/ | |||
private int xWidth; | |||
@SuppressWarnings("unused") | |||
@@ -488,6 +495,11 @@ public class HwmfMisc { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.addObjectTableEntry(this); | |||
} | |||
@Override | |||
public void applyObject(HwmfGraphics ctx) { | |||
HwmfDrawProperties p = ctx.getProperties(); | |||
p.setPenStyle(penStyle); | |||
p.setPenColor(colorRef); | |||
@@ -540,7 +552,7 @@ public class HwmfMisc { | |||
* </tr> | |||
* </table> | |||
*/ | |||
public static class WmfCreateBrushIndirect implements HwmfRecord { | |||
public static class WmfCreateBrushIndirect implements HwmfRecord, HwmfObjectTableEntry { | |||
private HwmfBrushStyle brushStyle; | |||
private HwmfColorRef colorRef; | |||
@@ -568,6 +580,11 @@ public class HwmfMisc { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.addObjectTableEntry(this); | |||
} | |||
@Override | |||
public void applyObject(HwmfGraphics ctx) { | |||
HwmfDrawProperties p = ctx.getProperties(); | |||
p.setBrushStyle(brushStyle); | |||
p.setBrushColor(colorRef); |
@@ -0,0 +1,28 @@ | |||
/* ==================================================================== | |||
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 org.apache.poi.hwmf.draw.HwmfGraphics; | |||
/** | |||
* Marker interface for Records, which should be added to the | |||
* WMF object table for further selection | |||
*/ | |||
public interface HwmfObjectTableEntry { | |||
public void applyObject(HwmfGraphics ctx); | |||
} |
@@ -81,7 +81,7 @@ public class HwmfPalette { | |||
/** | |||
* The META_CREATEPALETTE record creates a Palette Object | |||
*/ | |||
public static class WmfCreatePalette extends WmfPaletteParent { | |||
public static class WmfCreatePalette extends WmfPaletteParent implements HwmfObjectTableEntry { | |||
@Override | |||
public HwmfRecordType getRecordType() { | |||
return HwmfRecordType.createPalette; | |||
@@ -89,6 +89,11 @@ public class HwmfPalette { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.addObjectTableEntry(this); | |||
} | |||
@Override | |||
public void applyObject(HwmfGraphics ctx) { | |||
} | |||
} |
@@ -32,7 +32,7 @@ import org.apache.poi.util.BitFieldFactory; | |||
* 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 class HwmfPenStyle implements Cloneable { | |||
public enum HwmfLineCap { | |||
/** Rounded ends */ | |||
ROUND(0, BasicStroke.CAP_ROUND), | |||
@@ -168,4 +168,22 @@ public class HwmfPenStyle { | |||
public boolean isAlternateDash() { | |||
return SUBSECTION_ALTERNATE.isSet(flag); | |||
} | |||
/** | |||
* Creates a new object of the same class and with the | |||
* same contents as this object. | |||
* @return a clone of this instance. | |||
* @exception OutOfMemoryError if there is not enough memory. | |||
* @see java.lang.Cloneable | |||
*/ | |||
@Override | |||
public HwmfPenStyle clone() { | |||
try { | |||
return (HwmfPenStyle)super.clone(); | |||
} catch (CloneNotSupportedException e) { | |||
// this shouldn't happen, since we are Cloneable | |||
throw new InternalError(); | |||
} | |||
} | |||
} |
@@ -17,6 +17,7 @@ | |||
package org.apache.poi.hwmf.record; | |||
import java.awt.geom.Rectangle2D; | |||
import java.io.IOException; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
@@ -25,6 +26,8 @@ import org.apache.poi.util.LittleEndianInputStream; | |||
public class HwmfPlaceableHeader { | |||
public static int WMF_HEADER_MAGIC = 0x9AC6CDD7; | |||
final Rectangle2D bounds; | |||
protected HwmfPlaceableHeader(LittleEndianInputStream leis) throws IOException { | |||
/* | |||
* HWmf (2 bytes): The resource handle to the metafile, when the metafile is in memory. When | |||
@@ -41,6 +44,7 @@ public class HwmfPlaceableHeader { | |||
int y1 = leis.readShort(); | |||
int x2 = leis.readShort(); | |||
int y2 = leis.readShort(); | |||
bounds = new Rectangle2D.Double(x1, y1, x2-x1, y2-y1); | |||
/* | |||
* Inch (2 bytes): The number of logical units per inch used to represent the image. | |||
@@ -74,4 +78,8 @@ public class HwmfPlaceableHeader { | |||
return null; | |||
} | |||
} | |||
public Rectangle2D getBounds() { | |||
return bounds; | |||
} | |||
} |
@@ -23,76 +23,77 @@ package org.apache.poi.hwmf.record; | |||
* @see <a href="http://www.symantec.com/avcenter/reference/inside.the.windows.meta.file.format.pdf">Inside the Windows Meta File Format</a> | |||
*/ | |||
public enum HwmfRecordType { | |||
eof(0x0000, null), | |||
realizePalette(0x0035, HwmfPalette.WmfRealizePalette.class), | |||
setPalEntries(0x0037, HwmfPalette.WmfSetPaletteEntries.class), | |||
setBkMode(0x0102, HwmfMisc.WmfSetBkMode.class), | |||
setMapMode(0x0103, HwmfMisc.WmfSetMapMode.class), | |||
setRop2(0x0104, HwmfMisc.WmfSetRop2.class), | |||
setRelabs(0x0105, HwmfMisc.WmfSetRelabs.class), | |||
setPolyFillMode(0x0106, HwmfFill.WmfSetPolyfillMode.class), | |||
setStretchBltMode(0x0107, HwmfMisc.WmfSetStretchBltMode.class), | |||
setTextCharExtra(0x0108, HwmfText.WmfSetTextCharExtra.class), | |||
restoreDc(0x0127, HwmfMisc.WmfRestoreDc.class), | |||
resizePalette(0x0139, HwmfPalette.WmfResizePalette.class), | |||
dibCreatePatternBrush(0x0142, HwmfMisc.WmfDibCreatePatternBrush.class), | |||
setLayout(0x0149, HwmfMisc.WmfSetLayout.class), | |||
setBkColor(0x0201, HwmfMisc.WmfSetBkColor.class), | |||
setTextColor(0x0209, HwmfText.WmfSetTextColor.class), | |||
offsetViewportOrg(0x0211, HwmfWindowing.WmfOffsetViewportOrg.class), | |||
lineTo(0x0213, HwmfDraw.WmfLineTo.class), | |||
moveTo(0x0214, HwmfDraw.WmfMoveTo.class), | |||
offsetClipRgn(0x0220, HwmfWindowing.WmfOffsetClipRgn.class), | |||
fillRegion(0x0228, HwmfFill.WmfFillRegion.class), | |||
setMapperFlags(0x0231, HwmfMisc.WmfSetMapperFlags.class), | |||
selectPalette(0x0234, HwmfPalette.WmfSelectPalette.class), | |||
polygon(0x0324, HwmfDraw.WmfPolygon.class), | |||
polyline(0x0325, HwmfDraw.WmfPolyline.class), | |||
setTextJustification(0x020a, HwmfText.WmfSetTextJustification.class), | |||
setWindowOrg(0x020b, HwmfWindowing.WmfSetWindowOrg.class), | |||
setWindowExt(0x020c, HwmfWindowing.WmfSetWindowExt.class), | |||
setViewportOrg(0x020d, HwmfWindowing.WmfSetViewportOrg.class), | |||
setViewportExt(0x020e, HwmfWindowing.WmfSetViewportExt.class), | |||
offsetWindowOrg(0x020f, HwmfWindowing.WmfOffsetWindowOrg.class), | |||
scaleWindowExt(0x0410, HwmfWindowing.WmfScaleWindowExt.class), | |||
scaleViewportExt(0x0412, HwmfWindowing.WmfScaleViewportExt.class), | |||
excludeClipRect(0x0415, HwmfWindowing.WmfExcludeClipRect.class), | |||
intersectClipRect(0x0416, HwmfWindowing.WmfIntersectClipRect.class), | |||
ellipse(0x0418, HwmfDraw.WmfEllipse.class), | |||
floodFill(0x0419, HwmfFill.WmfFloodFill.class), | |||
frameRegion(0x0429, HwmfDraw.WmfFrameRegion.class), | |||
animatePalette(0x0436, HwmfPalette.WmfAnimatePalette.class), | |||
textOut(0x0521, HwmfText.WmfTextOut.class), | |||
polyPolygon(0x0538, HwmfDraw.WmfPolyPolygon.class), | |||
extFloodFill(0x0548, HwmfFill.WmfExtFloodFill.class), | |||
rectangle(0x041b, HwmfDraw.WmfRectangle.class), | |||
setPixel(0x041f, HwmfDraw.WmfSetPixel.class), | |||
roundRect(0x061c, HwmfDraw.WmfRoundRect.class), | |||
patBlt(0x061d, HwmfFill.WmfPatBlt.class), | |||
saveDc(0x001e, HwmfMisc.WmfSaveDc.class), | |||
pie(0x081a, HwmfDraw.WmfPie.class), | |||
stretchBlt(0x0b23, HwmfFill.WmfStretchBlt.class), | |||
escape(0x0626, HwmfEscape.class), | |||
invertRegion(0x012a, HwmfFill.WmfInvertRegion.class), | |||
paintRegion(0x012b, HwmfFill.WmfPaintRegion.class), | |||
selectClipRegion(0x012c, HwmfWindowing.WmfSelectClipRegion.class), | |||
selectObject(0x012d, HwmfDraw.WmfSelectObject.class), | |||
setTextAlign(0x012e, HwmfText.WmfSetTextAlign.class), | |||
arc(0x0817, HwmfDraw.WmfArc.class), | |||
chord(0x0830, HwmfDraw.WmfChord.class), | |||
bitBlt(0x0922, HwmfFill.WmfBitBlt.class), | |||
extTextOut(0x0a32, HwmfText.WmfExtTextOut.class), | |||
setDibToDev(0x0d33, HwmfFill.WmfSetDibToDev.class), | |||
dibBitBlt(0x0940, HwmfFill.WmfDibBitBlt.class), | |||
dibStretchBlt(0x0b41, HwmfFill.WmfDibStretchBlt.class), | |||
stretchDib(0x0f43, HwmfFill.WmfStretchDib.class), | |||
deleteObject(0x01f0, HwmfMisc.WmfDeleteObject.class), | |||
createPalette(0x00f7, HwmfPalette.WmfCreatePalette.class), | |||
createPatternBrush(0x01f9, HwmfMisc.WmfCreatePatternBrush.class), | |||
createPenIndirect(0x02fa, HwmfMisc.WmfCreatePenIndirect.class), | |||
createFontIndirect(0x02fb, HwmfText.WmfCreateFontIndirect.class), | |||
createBrushIndirect(0x02fc, HwmfMisc.WmfCreateBrushIndirect.class), | |||
createRegion(0x06ff, HwmfWindowing.WmfCreateRegion.class); | |||
eof(0x0000, null) | |||
,animatePalette(0x0436, HwmfPalette.WmfAnimatePalette.class) | |||
,arc(0x0817, HwmfDraw.WmfArc.class) | |||
,bitBlt(0x0922, HwmfFill.WmfBitBlt.class) | |||
,chord(0x0830, HwmfDraw.WmfChord.class) | |||
,createBrushIndirect(0x02fc, HwmfMisc.WmfCreateBrushIndirect.class) | |||
,createFontIndirect(0x02fb, HwmfText.WmfCreateFontIndirect.class) | |||
,createPalette(0x00f7, HwmfPalette.WmfCreatePalette.class) | |||
,createPatternBrush(0x01f9, HwmfMisc.WmfCreatePatternBrush.class) | |||
,createPenIndirect(0x02fa, HwmfMisc.WmfCreatePenIndirect.class) | |||
,createRegion(0x06ff, HwmfWindowing.WmfCreateRegion.class) | |||
,deleteObject(0x01f0, HwmfMisc.WmfDeleteObject.class) | |||
,dibBitBlt(0x0940, HwmfFill.WmfDibBitBlt.class) | |||
,dibCreatePatternBrush(0x0142, HwmfMisc.WmfDibCreatePatternBrush.class) | |||
,dibStretchBlt(0x0b41, HwmfFill.WmfDibStretchBlt.class) | |||
,ellipse(0x0418, HwmfDraw.WmfEllipse.class) | |||
,escape(0x0626, HwmfEscape.class) | |||
,excludeClipRect(0x0415, HwmfWindowing.WmfExcludeClipRect.class) | |||
,extFloodFill(0x0548, HwmfFill.WmfExtFloodFill.class) | |||
,extTextOut(0x0a32, HwmfText.WmfExtTextOut.class) | |||
,fillRegion(0x0228, HwmfFill.WmfFillRegion.class) | |||
,floodFill(0x0419, HwmfFill.WmfFloodFill.class) | |||
,frameRegion(0x0429, HwmfDraw.WmfFrameRegion.class) | |||
,intersectClipRect(0x0416, HwmfWindowing.WmfIntersectClipRect.class) | |||
,invertRegion(0x012a, HwmfFill.WmfInvertRegion.class) | |||
,lineTo(0x0213, HwmfDraw.WmfLineTo.class) | |||
,moveTo(0x0214, HwmfDraw.WmfMoveTo.class) | |||
,offsetClipRgn(0x0220, HwmfWindowing.WmfOffsetClipRgn.class) | |||
,offsetViewportOrg(0x0211, HwmfWindowing.WmfOffsetViewportOrg.class) | |||
,offsetWindowOrg(0x020f, HwmfWindowing.WmfOffsetWindowOrg.class) | |||
,paintRegion(0x012b, HwmfFill.WmfPaintRegion.class) | |||
,patBlt(0x061d, HwmfFill.WmfPatBlt.class) | |||
,pie(0x081a, HwmfDraw.WmfPie.class) | |||
,polygon(0x0324, HwmfDraw.WmfPolygon.class) | |||
,polyline(0x0325, HwmfDraw.WmfPolyline.class) | |||
,polyPolygon(0x0538, HwmfDraw.WmfPolyPolygon.class) | |||
,realizePalette(0x0035, HwmfPalette.WmfRealizePalette.class) | |||
,rectangle(0x041b, HwmfDraw.WmfRectangle.class) | |||
,resizePalette(0x0139, HwmfPalette.WmfResizePalette.class) | |||
,restoreDc(0x0127, HwmfMisc.WmfRestoreDc.class) | |||
,roundRect(0x061c, HwmfDraw.WmfRoundRect.class) | |||
,saveDc(0x001e, HwmfMisc.WmfSaveDc.class) | |||
,scaleViewportExt(0x0412, HwmfWindowing.WmfScaleViewportExt.class) | |||
,scaleWindowExt(0x0410, HwmfWindowing.WmfScaleWindowExt.class) | |||
,selectClipRegion(0x012c, HwmfWindowing.WmfSelectClipRegion.class) | |||
,selectObject(0x012d, HwmfDraw.WmfSelectObject.class) | |||
,selectPalette(0x0234, HwmfPalette.WmfSelectPalette.class) | |||
,setBkColor(0x0201, HwmfMisc.WmfSetBkColor.class) | |||
,setBkMode(0x0102, HwmfMisc.WmfSetBkMode.class) | |||
,setDibToDev(0x0d33, HwmfFill.WmfSetDibToDev.class) | |||
,setLayout(0x0149, HwmfMisc.WmfSetLayout.class) | |||
,setMapMode(0x0103, HwmfMisc.WmfSetMapMode.class) | |||
,setMapperFlags(0x0231, HwmfMisc.WmfSetMapperFlags.class) | |||
,setPalEntries(0x0037, HwmfPalette.WmfSetPaletteEntries.class) | |||
,setPixel(0x041f, HwmfDraw.WmfSetPixel.class) | |||
,setPolyFillMode(0x0106, HwmfFill.WmfSetPolyfillMode.class) | |||
,setRelabs(0x0105, HwmfMisc.WmfSetRelabs.class) | |||
,setRop2(0x0104, HwmfMisc.WmfSetRop2.class) | |||
,setStretchBltMode(0x0107, HwmfMisc.WmfSetStretchBltMode.class) | |||
,setTextAlign(0x012e, HwmfText.WmfSetTextAlign.class) | |||
,setTextCharExtra(0x0108, HwmfText.WmfSetTextCharExtra.class) | |||
,setTextColor(0x0209, HwmfText.WmfSetTextColor.class) | |||
,setTextJustification(0x020a, HwmfText.WmfSetTextJustification.class) | |||
,setViewportExt(0x020e, HwmfWindowing.WmfSetViewportExt.class) | |||
,setViewportOrg(0x020d, HwmfWindowing.WmfSetViewportOrg.class) | |||
,setWindowExt(0x020c, HwmfWindowing.WmfSetWindowExt.class) | |||
,setWindowOrg(0x020b, HwmfWindowing.WmfSetWindowOrg.class) | |||
,stretchBlt(0x0b23, HwmfFill.WmfStretchBlt.class) | |||
,stretchDib(0x0f43, HwmfFill.WmfStretchDib.class) | |||
,textOut(0x0521, HwmfText.WmfTextOut.class) | |||
; | |||
public int id; | |||
public Class<? extends HwmfRecord> clazz; |
@@ -391,7 +391,7 @@ public class HwmfText { | |||
} | |||
} | |||
public static class WmfCreateFontIndirect implements HwmfRecord { | |||
public static class WmfCreateFontIndirect implements HwmfRecord, HwmfObjectTableEntry { | |||
private HwmfFont font; | |||
@Override | |||
@@ -407,6 +407,11 @@ public class HwmfText { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.addObjectTableEntry(this); | |||
} | |||
@Override | |||
public void applyObject(HwmfGraphics ctx) { | |||
} | |||
} |
@@ -17,7 +17,10 @@ | |||
package org.apache.poi.hwmf.record; | |||
import java.awt.Shape; | |||
import java.awt.geom.Area; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.geom.Rectangle2D.Double; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
@@ -128,7 +131,9 @@ public class HwmfWindowing { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
Rectangle2D viewport = ctx.getProperties().getViewport(); | |||
ctx.getProperties().setViewportOrg(viewport.getX()+xOffset, viewport.getY()+yOffset); | |||
double x = (viewport == null) ? 0 : viewport.getX(); | |||
double y = (viewport == null) ? 0 : viewport.getY(); | |||
ctx.getProperties().setViewportOrg(x+xOffset, y+yOffset); | |||
} | |||
} | |||
@@ -163,6 +168,14 @@ public class HwmfWindowing { | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.getProperties().setWindowOrg(x, y); | |||
} | |||
public int getY() { | |||
return y; | |||
} | |||
public int getX() { | |||
return x; | |||
} | |||
} | |||
/** | |||
@@ -199,6 +212,14 @@ public class HwmfWindowing { | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.getProperties().setWindowExt(width, height); | |||
} | |||
public int getHeight() { | |||
return height; | |||
} | |||
public int getWidth() { | |||
return width; | |||
} | |||
} | |||
/** | |||
@@ -338,6 +359,9 @@ public class HwmfWindowing { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
Rectangle2D viewport = ctx.getProperties().getViewport(); | |||
if (viewport == null) { | |||
viewport = ctx.getProperties().getWindow(); | |||
} | |||
double width = viewport.getWidth() * xNum / xDenom; | |||
double height = viewport.getHeight() * yNum / yDenom; | |||
ctx.getProperties().setViewportExt(width, height); | |||
@@ -556,7 +580,7 @@ public class HwmfWindowing { | |||
} | |||
} | |||
public static class WmfCreateRegion implements HwmfRecord { | |||
public static class WmfCreateRegion implements HwmfRecord, HwmfObjectTableEntry { | |||
/** | |||
* A 16-bit signed integer. A value that MUST be ignored. | |||
*/ | |||
@@ -642,7 +666,32 @@ public class HwmfWindowing { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.addObjectTableEntry(this); | |||
} | |||
@Override | |||
public void applyObject(HwmfGraphics ctx) { | |||
Rectangle2D lastRect = null; | |||
Area scanLines = new Area(); | |||
int count = 0; | |||
for (WmfScanObject so : scanObjects) { | |||
int y = Math.min(so.top, so.bottom); | |||
int h = Math.abs(so.top - so.bottom - 1); | |||
for (int i=0; i<so.count/2; i++) { | |||
int x = Math.min(so.left_scanline[i], so.right_scanline[i]); | |||
int w = Math.abs(so.right_scanline[i] - so.left_scanline[i] - 1); | |||
lastRect = new Rectangle2D.Double(x,y,w,h); | |||
scanLines.add(new Area(lastRect)); | |||
count++; | |||
} | |||
} | |||
Shape region = null; | |||
if (count > 0) { | |||
region = (count == 1) ? lastRect : scanLines; | |||
} | |||
ctx.getProperties().setRegion(region); | |||
} | |||
} | |||
} |
@@ -17,6 +17,8 @@ | |||
package org.apache.poi.hwmf.usermodel; | |||
import java.awt.Graphics2D; | |||
import java.awt.geom.Rectangle2D; | |||
import java.io.BufferedInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
@@ -24,24 +26,25 @@ import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import org.apache.poi.hwmf.draw.HwmfGraphics; | |||
import org.apache.poi.hwmf.record.HwmfHeader; | |||
import org.apache.poi.hwmf.record.HwmfPlaceableHeader; | |||
import org.apache.poi.hwmf.record.HwmfRecord; | |||
import org.apache.poi.hwmf.record.HwmfRecordType; | |||
import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowExt; | |||
import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowOrg; | |||
import org.apache.poi.util.LittleEndianInputStream; | |||
public class HwmfPicture { | |||
List<HwmfRecord> records = new ArrayList<HwmfRecord>(); | |||
public List<HwmfRecord> getRecords() { | |||
return Collections.unmodifiableList(records); | |||
} | |||
final List<HwmfRecord> records = new ArrayList<HwmfRecord>(); | |||
final HwmfPlaceableHeader placeableHeader; | |||
final HwmfHeader header; | |||
public HwmfPicture(InputStream inputStream) throws IOException { | |||
BufferedInputStream bis = new BufferedInputStream(inputStream, 10000); | |||
LittleEndianInputStream leis = new LittleEndianInputStream(bis); | |||
HwmfPlaceableHeader placeableHeader = HwmfPlaceableHeader.readHeader(leis); | |||
HwmfHeader header = new HwmfHeader(leis); | |||
placeableHeader = HwmfPlaceableHeader.readHeader(leis); | |||
header = new HwmfHeader(leis); | |||
for (;;) { | |||
// recordSize in DWORDs | |||
@@ -76,5 +79,50 @@ public class HwmfPicture { | |||
} | |||
} | |||
public List<HwmfRecord> getRecords() { | |||
return Collections.unmodifiableList(records); | |||
} | |||
public void draw(Graphics2D ctx) { | |||
HwmfGraphics g = new HwmfGraphics(ctx, getBounds()); | |||
for (HwmfRecord r : records) { | |||
r.draw(g); | |||
} | |||
} | |||
/** | |||
* Returns the bounding box in device-independent units. Usually this is taken from the placeable header. | |||
* | |||
* @return the bounding box | |||
*/ | |||
public Rectangle2D getBounds() { | |||
if (placeableHeader != null) { | |||
return placeableHeader.getBounds(); | |||
} else { | |||
WmfSetWindowOrg wOrg = null; | |||
WmfSetWindowExt wExt = null; | |||
for (HwmfRecord r : getRecords()) { | |||
if (wOrg != null && wExt != null) { | |||
break; | |||
} | |||
if (r instanceof WmfSetWindowOrg) { | |||
wOrg = (WmfSetWindowOrg)r; | |||
} else if (r instanceof WmfSetWindowExt) { | |||
wExt = (WmfSetWindowExt)r; | |||
} | |||
} | |||
if (wOrg == null || wExt == null) { | |||
throw new RuntimeException("invalid wmf file - window records are incomplete."); | |||
} | |||
return new Rectangle2D.Double(wOrg.getX(), wOrg.getY(), wExt.getWidth(), wExt.getHeight()); | |||
} | |||
} | |||
public HwmfPlaceableHeader getPlaceableHeader() { | |||
return placeableHeader; | |||
} | |||
public HwmfHeader getHeader() { | |||
return header; | |||
} | |||
} |
@@ -19,6 +19,9 @@ package org.apache.poi.hwmf; | |||
import static org.junit.Assert.assertEquals; | |||
import java.awt.Graphics2D; | |||
import java.awt.RenderingHints; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.image.BufferedImage; | |||
import java.io.File; | |||
import java.io.FileFilter; | |||
@@ -55,6 +58,36 @@ public class TestHwmfParsing { | |||
List<HwmfRecord> records = wmf.getRecords(); | |||
assertEquals(581, records.size()); | |||
} | |||
@Test | |||
@Ignore | |||
public void paint() throws IOException { | |||
File f = POIDataSamples.getSlideShowInstance().getFile("santa.wmf"); | |||
FileInputStream fis = new FileInputStream(f); | |||
HwmfPicture wmf = new HwmfPicture(fis); | |||
fis.close(); | |||
Rectangle2D bounds = wmf.getBounds(); | |||
int width = 300; | |||
// keep aspect ratio for height | |||
int height = (int)(width*bounds.getHeight()/bounds.getWidth()); | |||
BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | |||
Graphics2D g = bufImg.createGraphics(); | |||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |||
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); | |||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); | |||
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); | |||
g.dispose(); | |||
ImageIO.write(bufImg, "PNG", new File("bla.png")); | |||
} | |||
@Test | |||
@Ignore |