git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1841134 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_1_0
@@ -20,10 +20,26 @@ package org.apache.poi.hemf.draw; | |||
import java.awt.Graphics2D; | |||
import java.awt.geom.Rectangle2D; | |||
import org.apache.poi.hwmf.draw.HwmfDrawProperties; | |||
import org.apache.poi.hwmf.draw.HwmfGraphics; | |||
public class HemfGraphics extends HwmfGraphics { | |||
public HemfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) { | |||
super(graphicsCtx,bbox); | |||
} | |||
@Override | |||
public HemfDrawProperties getProperties() { | |||
if (prop == null) { | |||
prop = new HemfDrawProperties(); | |||
} | |||
return (HemfDrawProperties)prop; | |||
} | |||
@Override | |||
public void saveProperties() { | |||
assert(prop != null); | |||
propStack.add(prop); | |||
prop = new HemfDrawProperties((HemfDrawProperties)prop); | |||
} | |||
} |
@@ -17,7 +17,10 @@ | |||
package org.apache.poi.hemf.record.emf; | |||
import java.awt.geom.AffineTransform; | |||
import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_NULL; | |||
import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_SOLID; | |||
import java.awt.Color; | |||
import java.awt.geom.Dimension2D; | |||
import java.awt.geom.Path2D; | |||
import java.awt.geom.PathIterator; | |||
@@ -25,10 +28,13 @@ import java.awt.geom.Point2D; | |||
import java.awt.geom.Rectangle2D; | |||
import java.io.IOException; | |||
import org.apache.poi.hemf.draw.HemfDrawProperties; | |||
import org.apache.poi.hemf.draw.HemfGraphics; | |||
import org.apache.poi.hwmf.draw.HwmfGraphics; | |||
import org.apache.poi.hwmf.record.HwmfColorRef; | |||
import org.apache.poi.hwmf.record.HwmfDraw; | |||
import org.apache.poi.hwmf.record.HwmfDraw.WmfSelectObject; | |||
import org.apache.poi.hwmf.record.HwmfPenStyle; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
import org.apache.poi.util.LittleEndianInputStream; | |||
@@ -40,6 +46,12 @@ public class HemfDraw { | |||
*/ | |||
public static class EmfSelectObject extends WmfSelectObject implements HemfRecord { | |||
private static final HwmfColorRef WHITE = new HwmfColorRef(Color.WHITE); | |||
private static final HwmfColorRef LTGRAY = new HwmfColorRef(new Color(0x00C0C0C0)); | |||
private static final HwmfColorRef GRAY = new HwmfColorRef(new Color(0x00808080)); | |||
private static final HwmfColorRef DKGRAY = new HwmfColorRef(new Color(0x00404040)); | |||
private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK); | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.selectObject; | |||
@@ -52,6 +64,125 @@ public class HemfDraw { | |||
objectIndex = leis.readInt(); | |||
return LittleEndianConsts.INT_SIZE; | |||
} | |||
@Override | |||
public void draw(HemfGraphics ctx) { | |||
if ((objectIndex & 0x80000000) != 0) { | |||
selectStockObject(ctx); | |||
} else { | |||
super.draw(ctx); | |||
} | |||
} | |||
private void selectStockObject(HemfGraphics ctx) { | |||
final HemfDrawProperties prop = ctx.getProperties(); | |||
switch (objectIndex) { | |||
case 0x80000000: | |||
// WHITE_BRUSH - A white, solid-color brush | |||
// BrushStyle: BS_SOLID | |||
// Color: 0x00FFFFFF | |||
prop.setBrushColor(WHITE); | |||
prop.setBrushStyle(BS_SOLID); | |||
break; | |||
case 0x80000001: | |||
// LTGRAY_BRUSH - A light gray, solid-color brush | |||
// BrushStyle: BS_SOLID | |||
// Color: 0x00C0C0C0 | |||
prop.setBrushColor(LTGRAY); | |||
prop.setBrushStyle(BS_SOLID); | |||
break; | |||
case 0x80000002: | |||
// GRAY_BRUSH - A gray, solid-color brush | |||
// BrushStyle: BS_SOLID | |||
// Color: 0x00808080 | |||
prop.setBrushColor(GRAY); | |||
prop.setBrushStyle(BS_SOLID); | |||
break; | |||
case 0x80000003: | |||
// DKGRAY_BRUSH - A dark gray, solid color brush | |||
// BrushStyle: BS_SOLID | |||
// Color: 0x00404040 | |||
prop.setBrushColor(DKGRAY); | |||
prop.setBrushStyle(BS_SOLID); | |||
break; | |||
case 0x80000004: | |||
// BLACK_BRUSH - A black, solid color brush | |||
// BrushStyle: BS_SOLID | |||
// Color: 0x00000000 | |||
prop.setBrushColor(BLACK); | |||
prop.setBrushStyle(BS_SOLID); | |||
break; | |||
case 0x80000005: | |||
// NULL_BRUSH - A null brush | |||
// BrushStyle: BS_NULL | |||
prop.setBrushStyle(BS_NULL); | |||
break; | |||
case 0x80000006: | |||
// WHITE_PEN - A white, solid-color pen | |||
// PenStyle: PS_COSMETIC + PS_SOLID | |||
// ColorRef: 0x00FFFFFF | |||
prop.setPenStyle(HwmfPenStyle.valueOf(0)); | |||
prop.setPenWidth(1); | |||
prop.setPenColor(WHITE); | |||
break; | |||
case 0x80000007: | |||
// BLACK_PEN - A black, solid-color pen | |||
// PenStyle: PS_COSMETIC + PS_SOLID | |||
// ColorRef: 0x00000000 | |||
prop.setPenStyle(HwmfPenStyle.valueOf(0)); | |||
prop.setPenWidth(1); | |||
prop.setPenColor(BLACK); | |||
break; | |||
case 0x80000008: | |||
// NULL_PEN - A null pen | |||
// PenStyle: PS_NULL | |||
prop.setPenStyle(HwmfPenStyle.valueOf(HwmfPenStyle.HwmfLineDash.NULL.wmfFlag)); | |||
break; | |||
case 0x8000000A: | |||
// OEM_FIXED_FONT - A fixed-width, OEM character set | |||
// Charset: OEM_CHARSET | |||
// PitchAndFamily: FF_DONTCARE + FIXED_PITCH | |||
break; | |||
case 0x8000000B: | |||
// ANSI_FIXED_FONT - A fixed-width font | |||
// Charset: ANSI_CHARSET | |||
// PitchAndFamily: FF_DONTCARE + FIXED_PITCH | |||
break; | |||
case 0x8000000C: | |||
// ANSI_VAR_FONT - A variable-width font | |||
// Charset: ANSI_CHARSET | |||
// PitchAndFamily: FF_DONTCARE + VARIABLE_PITCH | |||
break; | |||
case 0x8000000D: | |||
// SYSTEM_FONT - A font that is guaranteed to be available in the operating system | |||
break; | |||
case 0x8000000E: | |||
// DEVICE_DEFAULT_FONT | |||
// The default font that is provided by the graphics device driver for the current output device | |||
break; | |||
case 0x8000000F: | |||
// DEFAULT_PALETTE | |||
// The default palette that is defined for the current output device. | |||
break; | |||
case 0x80000010: | |||
// SYSTEM_FIXED_FONT | |||
// A fixed-width font that is guaranteed to be available in the operating system. | |||
break; | |||
case 0x80000011: | |||
// DEFAULT_GUI_FONT | |||
// The default font that is used for user interface objects such as menus and dialog boxes. | |||
break; | |||
case 0x80000012: | |||
// DC_BRUSH | |||
// The solid-color brush that is currently selected in the playback device context. | |||
break; | |||
case 0x80000013: | |||
// DC_PEN | |||
// The solid-color pen that is currently selected in the playback device context. | |||
break; | |||
} | |||
} | |||
} | |||
@@ -717,8 +848,98 @@ public class HemfDraw { | |||
protected long readPoint(LittleEndianInputStream leis, Point2D point) { | |||
return readPointS(leis, point); | |||
} | |||
} | |||
public static class EmfBeginPath implements HemfRecord { | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.beginPath; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
return 0; | |||
} | |||
} | |||
public static class EmfEndPath implements HemfRecord { | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.endPath; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
return 0; | |||
} | |||
} | |||
public static class EmfAbortPath implements HemfRecord { | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.abortPath; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
return 0; | |||
} | |||
} | |||
public static class EmfCloseFigure implements HemfRecord { | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.closeFigure; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
return 0; | |||
} | |||
} | |||
public static class EmfFlattenPath implements HemfRecord { | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.flattenPath; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
return 0; | |||
} | |||
} | |||
public static class EmfWidenPath implements HemfRecord { | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.widenPath; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
return 0; | |||
} | |||
} | |||
/** | |||
* The EMR_STROKEPATH record renders the specified path by using the current pen. | |||
*/ | |||
public static class EmfStrokePath implements HemfRecord { | |||
protected final Rectangle2D bounds = new Rectangle2D.Double(); | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.strokePath; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
// A 128-bit WMF RectL object, which specifies bounding rectangle, in device units | |||
return readRectL(leis, bounds); | |||
} | |||
} | |||
static long readRectL(LittleEndianInputStream leis, Rectangle2D bounds) { | |||
/* A 32-bit signed integer that defines the x coordinate, in logical coordinates, | |||
* of the ... corner of the rectangle. |
@@ -617,4 +617,23 @@ public class HemfFill { | |||
return 6 * LittleEndian.INT_SIZE; | |||
} | |||
/** | |||
* The EMR_FILLPATH record closes any open figures in the current path and fills the path's interior by | |||
* using the current brush and polygon-filling mode. | |||
*/ | |||
public static class EmfFillPath implements HemfRecord { | |||
protected final Rectangle2D bounds = new Rectangle2D.Double(); | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.fillPath; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
// A 128-bit WMF RectL object, which specifies bounding rectangle, in device units | |||
return readRectL(leis, bounds); | |||
} | |||
} | |||
} |
@@ -17,10 +17,11 @@ | |||
package org.apache.poi.hemf.record.emf; | |||
import static org.apache.poi.hemf.record.emf.HemfDraw.readDimensionInt; | |||
import static org.apache.poi.hemf.record.emf.HemfDraw.readPointL; | |||
import static org.apache.poi.hemf.record.emf.HemfFill.readBitmap; | |||
import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE; | |||
import java.awt.geom.Point2D; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
@@ -37,7 +38,6 @@ import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode; | |||
import org.apache.poi.hwmf.record.HwmfPalette.PaletteEntry; | |||
import org.apache.poi.hwmf.record.HwmfPenStyle; | |||
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash; | |||
import org.apache.poi.util.IOUtils; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
import org.apache.poi.util.LittleEndianInputStream; | |||
@@ -56,22 +56,22 @@ public class HemfMisc { | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
// A 32-bit unsigned integer that specifies the number of palette entries. | |||
int nPalEntries = (int)leis.readUInt(); | |||
int nPalEntries = (int) leis.readUInt(); | |||
// A 32-bit unsigned integer that specifies the offset to the palette entries from the start of this record. | |||
int offPalEntries = (int)leis.readUInt(); | |||
int offPalEntries = (int) leis.readUInt(); | |||
int size = 2*LittleEndianConsts.INT_SIZE; | |||
int undefinedSpace1 = (int)(offPalEntries - size - HEADER_SIZE); | |||
int size = 2 * LittleEndianConsts.INT_SIZE; | |||
int undefinedSpace1 = (int) (offPalEntries - size - HEADER_SIZE); | |||
assert (undefinedSpace1 >= 0); | |||
leis.skipFully(undefinedSpace1); | |||
size += undefinedSpace1; | |||
for (int i=0; i<nPalEntries; i++) { | |||
for (int i = 0; i < nPalEntries; i++) { | |||
PaletteEntry pe = new PaletteEntry(); | |||
size += pe.init(leis); | |||
} | |||
int undefinedSpace2 = (int)(recordSize - size - LittleEndianConsts.INT_SIZE); | |||
int undefinedSpace2 = (int) (recordSize - size - LittleEndianConsts.INT_SIZE); | |||
assert (undefinedSpace2 >= 0); | |||
leis.skipFully(undefinedSpace2); | |||
size += undefinedSpace2; | |||
@@ -81,7 +81,7 @@ public class HemfMisc { | |||
// LogPaletteEntry objects, if they exist, MUST precede this field. | |||
long sizeLast = leis.readUInt(); | |||
size += LittleEndianConsts.INT_SIZE; | |||
assert ((sizeLast-HEADER_SIZE) == recordSize && recordSize == size); | |||
assert ((sizeLast - HEADER_SIZE) == recordSize && recordSize == size); | |||
return size; | |||
} | |||
@@ -179,7 +179,7 @@ public class HemfMisc { | |||
* A 32-bit unsigned integer that specifies the background mode | |||
* and MUST be in the BackgroundMode (section 2.1.4) enumeration | |||
*/ | |||
bkMode = HwmfBkMode.valueOf((int)leis.readUInt()); | |||
bkMode = HwmfBkMode.valueOf((int) leis.readUInt()); | |||
return LittleEndianConsts.INT_SIZE; | |||
} | |||
} | |||
@@ -195,7 +195,7 @@ public class HemfMisc { | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
return super.init(leis, recordSize, (int)recordId); | |||
return super.init(leis, recordSize, (int) recordId); | |||
} | |||
} | |||
@@ -212,7 +212,7 @@ public class HemfMisc { | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
// A 32-bit unsigned integer whose definition MUST be in the MapMode enumeration | |||
mapMode = HwmfMapMode.valueOf((int)leis.readUInt()); | |||
mapMode = HwmfMapMode.valueOf((int) leis.readUInt()); | |||
return LittleEndianConsts.INT_SIZE; | |||
} | |||
} | |||
@@ -230,7 +230,7 @@ public class HemfMisc { | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
// A 32-bit unsigned integer that specifies the raster operation mode and | |||
// MUST be in the WMF Binary Raster Op enumeration | |||
drawMode = HwmfBinaryRasterOp.valueOf((int)leis.readUInt()); | |||
drawMode = HwmfBinaryRasterOp.valueOf((int) leis.readUInt()); | |||
return LittleEndianConsts.INT_SIZE; | |||
} | |||
} | |||
@@ -250,12 +250,14 @@ public class HemfMisc { | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
// A 32-bit unsigned integer that specifies the stretch mode and MAY be | |||
// in the StretchMode enumeration. | |||
stretchBltMode = StretchBltMode.valueOf((int)leis.readUInt()); | |||
stretchBltMode = StretchBltMode.valueOf((int) leis.readUInt()); | |||
return LittleEndianConsts.INT_SIZE; | |||
} | |||
} | |||
/** The EMR_CREATEBRUSHINDIRECT record defines a logical brush for graphics operations. */ | |||
/** | |||
* The EMR_CREATEBRUSHINDIRECT record defines a logical brush for graphics operations. | |||
*/ | |||
public static class EmfCreateBrushIndirect extends HwmfMisc.WmfCreateBrushIndirect implements HemfRecord { | |||
/** | |||
* A 32-bit unsigned integer that specifies the index of the logical brush object in the | |||
@@ -270,13 +272,13 @@ public class HemfMisc { | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
brushIdx = (int)leis.readUInt(); | |||
brushIdx = (int) leis.readUInt(); | |||
brushStyle = HwmfBrushStyle.valueOf((int)leis.readUInt()); | |||
brushStyle = HwmfBrushStyle.valueOf((int) leis.readUInt()); | |||
colorRef = new HwmfColorRef(); | |||
int size = colorRef.init(leis); | |||
brushHatch = HwmfHatchStyle.valueOf((int)leis.readUInt()); | |||
return size+3*LittleEndianConsts.INT_SIZE; | |||
brushHatch = HwmfHatchStyle.valueOf((int) leis.readUInt()); | |||
return size + 3 * LittleEndianConsts.INT_SIZE; | |||
} | |||
} | |||
@@ -294,12 +296,14 @@ public class HemfMisc { | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
objectIndex = (int)leis.readUInt(); | |||
objectIndex = (int) leis.readUInt(); | |||
return LittleEndianConsts.INT_SIZE; | |||
} | |||
} | |||
/** The EMR_CREATEPEN record defines a logical pen for graphics operations. */ | |||
/** | |||
* The EMR_CREATEPEN record defines a logical pen for graphics operations. | |||
*/ | |||
public static class EmfCreatePen extends HwmfMisc.WmfCreatePenIndirect implements HemfRecord { | |||
/** | |||
* A 32-bit unsigned integer that specifies the index of the logical palette object | |||
@@ -315,11 +319,11 @@ public class HemfMisc { | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
penIndex = (int)leis.readUInt(); | |||
penIndex = (int) leis.readUInt(); | |||
// A 32-bit unsigned integer that specifies the PenStyle. | |||
// The value MUST be defined from the PenStyle enumeration table | |||
penStyle = HwmfPenStyle.valueOf((int)leis.readUInt()); | |||
penStyle = HwmfPenStyle.valueOf((int) leis.readUInt()); | |||
int widthX = leis.readInt(); | |||
int widthY = leis.readInt(); | |||
@@ -327,7 +331,7 @@ public class HemfMisc { | |||
int size = colorRef.init(leis); | |||
return size + 4*LittleEndianConsts.INT_SIZE; | |||
return size + 4 * LittleEndianConsts.INT_SIZE; | |||
} | |||
} | |||
@@ -349,27 +353,27 @@ public class HemfMisc { | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
final int startIdx = leis.getReadIndex(); | |||
penIndex = (int)leis.readUInt(); | |||
penIndex = (int) leis.readUInt(); | |||
// A 32-bit unsigned integer that specifies the offset from the start of this | |||
// record to the DIB header, if the record contains a DIB. | |||
int offBmi = (int)leis.readUInt(); | |||
int offBmi = (int) leis.readUInt(); | |||
// A 32-bit unsigned integer that specifies the size of the DIB header, if the | |||
// record contains a DIB. | |||
int cbBmi = (int)leis.readUInt(); | |||
int cbBmi = (int) leis.readUInt(); | |||
// A 32-bit unsigned integer that specifies the offset from the start of this | |||
// record to the DIB bits, if the record contains a DIB. | |||
int offBits = (int)leis.readUInt(); | |||
int offBits = (int) leis.readUInt(); | |||
// A 32-bit unsigned integer that specifies the size of the DIB bits, if the record | |||
// contains a DIB. | |||
int cbBits = (int)leis.readUInt(); | |||
int cbBits = (int) leis.readUInt(); | |||
// A 32-bit unsigned integer that specifies the PenStyle. | |||
// The value MUST be defined from the PenStyle enumeration table | |||
penStyle = HwmfPenStyle.valueOf((int)leis.readUInt()); | |||
penStyle = HwmfPenStyle.valueOf((int) leis.readUInt()); | |||
// A 32-bit unsigned integer that specifies the width of the line drawn by the pen. | |||
// If the pen type in the PenStyle field is PS_GEOMETRIC, this value is the width in logical | |||
@@ -383,7 +387,7 @@ public class HemfMisc { | |||
// If the pen type in the PenStyle field is PS_GEOMETRIC, this value MUST be either BS_SOLID or BS_HATCHED. | |||
// The value of this field can be BS_NULL, but only if the line style specified in PenStyle is PS_NULL. | |||
// The BS_NULL style SHOULD be used to specify a brush that has no effect | |||
brushStyle = HwmfBrushStyle.valueOf((int)leis.readUInt()); | |||
brushStyle = HwmfBrushStyle.valueOf((int) leis.readUInt()); | |||
int size = 8 * LittleEndianConsts.INT_SIZE; | |||
@@ -393,10 +397,10 @@ public class HemfMisc { | |||
// The number of elements in the array specified in the StyleEntry | |||
// field. This value SHOULD be zero if PenStyle does not specify PS_USERSTYLE. | |||
final int numStyleEntries = (int)leis.readUInt(); | |||
size += 2*LittleEndianConsts.INT_SIZE; | |||
final int numStyleEntries = (int) leis.readUInt(); | |||
size += 2 * LittleEndianConsts.INT_SIZE; | |||
assert(numStyleEntries == 0 || penStyle.getLineDash() == HwmfLineDash.USERSTYLE); | |||
assert (numStyleEntries == 0 || penStyle.getLineDash() == HwmfLineDash.USERSTYLE); | |||
// An optional array of 32-bit unsigned integers that defines the lengths of | |||
// dashes and gaps in the line drawn by this pen, when the value of PenStyle is | |||
@@ -409,8 +413,8 @@ public class HemfMisc { | |||
styleEntry = new int[numStyleEntries]; | |||
for (int i=0; i<numStyleEntries; i++) { | |||
styleEntry[i] = (int)leis.readUInt(); | |||
for (int i = 0; i < numStyleEntries; i++) { | |||
styleEntry[i] = (int) leis.readUInt(); | |||
} | |||
size += numStyleEntries * LittleEndianConsts.INT_SIZE; | |||
@@ -435,7 +439,7 @@ public class HemfMisc { | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
miterLimit = (int)leis.readUInt(); | |||
miterLimit = (int) leis.readUInt(); | |||
return LittleEndianConsts.INT_SIZE; | |||
} | |||
@@ -444,4 +448,19 @@ public class HemfMisc { | |||
ctx.getProperties().setPenMiterLimit(miterLimit); | |||
} | |||
} | |||
public static class EmfSetBrushOrgEx implements HemfRecord { | |||
protected final Point2D origin = new Point2D.Double(); | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.setBrushOrgEx; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
return readPointL(leis, origin); | |||
} | |||
} | |||
} |
@@ -36,7 +36,7 @@ public enum HemfRecordType { | |||
setWindowOrgEx(0x0000000A, HemfWindowing.EmfSetWindowOrgEx::new), | |||
setViewportExtEx(0x0000000B, HemfWindowing.EmfSetViewportExtEx::new), | |||
setViewportOrgEx(0x0000000C, HemfWindowing.EmfSetViewportOrgEx::new), | |||
setbrushorgex(0x0000000D, UnimplementedHemfRecord::new), | |||
setBrushOrgEx(0x0000000D, HemfMisc.EmfSetBrushOrgEx::new), | |||
eof(0x0000000E, HemfMisc.EmfEof::new), | |||
setPixelV(0x0000000F, HemfDraw.EmfSetPixelV::new), | |||
setMapperFlags(0x00000010, HemfMisc.EmfSetMapperFlags::new), | |||
@@ -82,16 +82,16 @@ public enum HemfRecordType { | |||
polyDraw(0x00000038, HemfDraw.EmfPolyDraw::new), | |||
setarcdirection(0x00000039, UnimplementedHemfRecord::new), | |||
setMiterLimit(0x0000003A, HemfMisc.EmfSetMiterLimit::new), | |||
beginpath(0x0000003B, UnimplementedHemfRecord::new), | |||
endpath(0x0000003C, UnimplementedHemfRecord::new), | |||
closefigure(0x0000003D, UnimplementedHemfRecord::new), | |||
fillpath(0x0000003E, UnimplementedHemfRecord::new), | |||
beginPath(0x0000003B, HemfDraw.EmfBeginPath::new), | |||
endPath(0x0000003C, HemfDraw.EmfEndPath::new), | |||
closeFigure(0x0000003D, HemfDraw.EmfCloseFigure::new), | |||
fillPath(0x0000003E, HemfFill.EmfFillPath::new), | |||
strokeandfillpath(0x0000003F, UnimplementedHemfRecord::new), | |||
strokepath(0x00000040, UnimplementedHemfRecord::new), | |||
flattenpath(0x00000041, UnimplementedHemfRecord::new), | |||
widenpath(0x00000042, UnimplementedHemfRecord::new), | |||
selectclippath(0x00000043, UnimplementedHemfRecord::new), | |||
abortpath(0x00000044, UnimplementedHemfRecord::new), | |||
strokePath(0x00000040, HemfDraw.EmfStrokePath::new), | |||
flattenPath(0x00000041, HemfDraw.EmfFlattenPath::new), | |||
widenPath(0x00000042, HemfDraw.EmfWidenPath::new), | |||
selectClipPath(0x00000043, HemfWindowing.EmfSelectClipPath::new), | |||
abortPath(0x00000044, HemfDraw.EmfAbortPath::new), | |||
// no 45 ?! | |||
comment(0x00000046, HemfComment.EmfComment::new), | |||
fillRgn(0x00000047, HemfFill.EmfFillRgn::new), |
@@ -19,6 +19,7 @@ package org.apache.poi.hemf.record.emf; | |||
import java.io.IOException; | |||
import org.apache.poi.hemf.record.emf.HemfFill.HemfRegionMode; | |||
import org.apache.poi.hwmf.record.HwmfWindowing; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
import org.apache.poi.util.LittleEndianInputStream; | |||
@@ -197,4 +198,27 @@ public class HemfWindowing { | |||
return 4*LittleEndianConsts.INT_SIZE; | |||
} | |||
} | |||
} | |||
/** | |||
* The EMR_SELECTCLIPPATH record specifies the current path as a clipping region for a playback | |||
* device context, combining the new region with any existing clipping region using the specified mode. | |||
*/ | |||
public static class EmfSelectClipPath implements HemfRecord { | |||
protected HemfRegionMode regionMode; | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.selectClipPath; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
// A 32-bit unsigned integer that specifies the way to use the path. | |||
// The value MUST be in the RegionMode enumeration | |||
regionMode = HemfRegionMode.valueOf(leis.readInt()); | |||
return LittleEndianConsts.INT_SIZE; | |||
} | |||
} | |||
} |
@@ -27,19 +27,16 @@ import org.apache.poi.util.LittleEndianInputStream; | |||
@Internal | |||
public class UnimplementedHemfRecord implements HemfRecord { | |||
private long recordId; | |||
public UnimplementedHemfRecord() { | |||
} | |||
private HemfRecordType recordType; | |||
@Override | |||
public HemfRecordType getEmfRecordType() { | |||
return HemfRecordType.getById(recordId); | |||
return recordType; | |||
} | |||
@Override | |||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { | |||
this.recordId = recordId; | |||
recordType = HemfRecordType.getById(recordId); | |||
long skipped = IOUtils.skipFully(leis, recordSize); | |||
if (skipped < recordSize) { | |||
throw new IOException("End of stream reached before record read"); |
@@ -34,7 +34,7 @@ public class HemfPlusHeader implements HemfPlusRecord { | |||
private long logicalDpiY; | |||
@Override | |||
public HemfPlusRecordType getRecordType() { | |||
public HemfPlusRecordType getEmfPlusRecordType() { | |||
return HemfPlusRecordType.header; | |||
} | |||
@@ -27,7 +27,7 @@ import org.apache.poi.util.LittleEndianInputStream; | |||
@Internal | |||
public interface HemfPlusRecord { | |||
HemfPlusRecordType getRecordType(); | |||
HemfPlusRecordType getEmfPlusRecordType(); | |||
int getFlags(); | |||
@@ -57,7 +57,7 @@ public class HemfPlusRecordIterator implements Iterator<HemfPlusRecord> { | |||
} | |||
private HemfPlusRecord _next() { | |||
if (currentRecord != null && HemfPlusRecordType.eof == currentRecord.getRecordType()) { | |||
if (currentRecord != null && HemfPlusRecordType.eof == currentRecord.getEmfPlusRecordType()) { | |||
return null; | |||
} | |||
// A 16-bit unsigned integer that identifies this record type |
@@ -29,13 +29,13 @@ public class UnimplementedHemfPlusRecord implements HemfPlusRecord { | |||
private static final int MAX_RECORD_LENGTH = 1_000_000; | |||
private long recordId; | |||
private HemfPlusRecordType recordType; | |||
private int flags; | |||
private byte[] recordBytes; | |||
@Override | |||
public HemfPlusRecordType getRecordType() { | |||
return HemfPlusRecordType.getById(recordId); | |||
public HemfPlusRecordType getEmfPlusRecordType() { | |||
return recordType; | |||
} | |||
@Override | |||
@@ -45,7 +45,7 @@ public class UnimplementedHemfPlusRecord implements HemfPlusRecord { | |||
@Override | |||
public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException { | |||
this.recordId = recordId; | |||
recordType = HemfPlusRecordType.getById(recordId); | |||
this.flags = flags; | |||
recordBytes = IOUtils.safelyAllocate(dataSize, MAX_RECORD_LENGTH); | |||
leis.readFully(recordBytes); |
@@ -52,11 +52,12 @@ import org.apache.poi.util.LocaleUtil; | |||
public class HwmfGraphics { | |||
protected final List<HwmfDrawProperties> propStack = new LinkedList<>(); | |||
protected HwmfDrawProperties prop; | |||
private static final Charset DEFAULT_CHARSET = LocaleUtil.CHARSET_1252; | |||
private final Graphics2D graphicsCtx; | |||
private final List<HwmfDrawProperties> propStack = new LinkedList<>(); | |||
private HwmfDrawProperties prop = new HwmfDrawProperties(); | |||
private List<HwmfObjectTableEntry> objectTable = new ArrayList<>(); | |||
private final List<HwmfObjectTableEntry> objectTable = new ArrayList<>(); | |||
/** Bounding box from the placeable header */ | |||
private final Rectangle2D bbox; | |||
private final AffineTransform initialAT; | |||
@@ -75,11 +76,14 @@ public class HwmfGraphics { | |||
} | |||
public HwmfDrawProperties getProperties() { | |||
if (prop == null) { | |||
prop = new HwmfDrawProperties(); | |||
} | |||
return prop; | |||
} | |||
public void draw(Shape shape) { | |||
HwmfLineDash lineDash = prop.getPenStyle().getLineDash(); | |||
HwmfLineDash lineDash = getProperties().getPenStyle().getLineDash(); | |||
if (lineDash == HwmfLineDash.NULL) { | |||
// line is not drawn | |||
return; | |||
@@ -89,22 +93,22 @@ public class HwmfGraphics { | |||
// first draw a solid background line (depending on bkmode) | |||
// only makes sense if the line is not solid | |||
if (prop.getBkMode() == HwmfBkMode.OPAQUE && (lineDash != HwmfLineDash.SOLID && lineDash != HwmfLineDash.INSIDEFRAME)) { | |||
if (getProperties().getBkMode() == HwmfBkMode.OPAQUE && (lineDash != HwmfLineDash.SOLID && lineDash != HwmfLineDash.INSIDEFRAME)) { | |||
graphicsCtx.setStroke(new BasicStroke(stroke.getLineWidth())); | |||
graphicsCtx.setColor(prop.getBackgroundColor().getColor()); | |||
graphicsCtx.setColor(getProperties().getBackgroundColor().getColor()); | |||
graphicsCtx.draw(shape); | |||
} | |||
// then draw the (dashed) line | |||
graphicsCtx.setStroke(stroke); | |||
graphicsCtx.setColor(prop.getPenColor().getColor()); | |||
graphicsCtx.setColor(getProperties().getPenColor().getColor()); | |||
graphicsCtx.draw(shape); | |||
} | |||
public void fill(Shape shape) { | |||
if (prop.getBrushStyle() != HwmfBrushStyle.BS_NULL) { | |||
if (getProperties().getBrushStyle() != HwmfBrushStyle.BS_NULL) { | |||
// GeneralPath gp = new GeneralPath(shape); | |||
// gp.setWindingRule(prop.getPolyfillMode().awtFlag); | |||
// gp.setWindingRule(getProperties().getPolyfillMode().awtFlag); | |||
graphicsCtx.setPaint(getFill()); | |||
graphicsCtx.fill(shape); | |||
} | |||
@@ -114,14 +118,14 @@ public class HwmfGraphics { | |||
protected BasicStroke getStroke() { | |||
// TODO: fix line width calculation | |||
float width = (float)prop.getPenWidth(); | |||
float width = (float)getProperties().getPenWidth(); | |||
if (width == 0) { | |||
width = 1; | |||
} | |||
HwmfPenStyle ps = prop.getPenStyle(); | |||
HwmfPenStyle ps = getProperties().getPenStyle(); | |||
int cap = ps.getLineCap().awtFlag; | |||
int join = ps.getLineJoin().awtFlag; | |||
float miterLimit = (float)prop.getPenMiterLimit(); | |||
float miterLimit = (float)getProperties().getPenMiterLimit(); | |||
float dashes[] = ps.getLineDash().dashes; | |||
boolean dashAlt = ps.isAlternateDash(); | |||
// This value is not an integer index into the dash pattern array. | |||
@@ -132,7 +136,7 @@ public class HwmfGraphics { | |||
} | |||
protected Paint getFill() { | |||
switch (prop.getBrushStyle()) { | |||
switch (getProperties().getBrushStyle()) { | |||
default: | |||
case BS_INDEXED: | |||
case BS_PATTERN8X8: | |||
@@ -148,20 +152,20 @@ public class HwmfGraphics { | |||
} | |||
protected Paint getSolidFill() { | |||
return prop.getBrushColor().getColor(); | |||
return getProperties().getBrushColor().getColor(); | |||
} | |||
protected Paint getHatchedFill() { | |||
int dim = 7, mid = 3; | |||
BufferedImage bi = new BufferedImage(dim, dim, BufferedImage.TYPE_4BYTE_ABGR); | |||
Graphics2D g = bi.createGraphics(); | |||
Color c = (prop.getBkMode() == HwmfBkMode.TRANSPARENT) | |||
Color c = (getProperties().getBkMode() == HwmfBkMode.TRANSPARENT) | |||
? new Color(0, true) | |||
: prop.getBackgroundColor().getColor(); | |||
: getProperties().getBackgroundColor().getColor(); | |||
g.setColor(c); | |||
g.fillRect(0, 0, dim, dim); | |||
g.setColor(prop.getBrushColor().getColor()); | |||
HwmfHatchStyle h = prop.getBrushHatch(); | |||
g.setColor(getProperties().getBrushColor().getColor()); | |||
HwmfHatchStyle h = getProperties().getBrushHatch(); | |||
if (h == HwmfHatchStyle.HS_HORIZONTAL || h == HwmfHatchStyle.HS_CROSS) { | |||
g.drawLine(0, mid, dim, mid); | |||
} | |||
@@ -179,7 +183,7 @@ public class HwmfGraphics { | |||
} | |||
protected Paint getPatternPaint() { | |||
BufferedImage bi = prop.getBrushBitmap(); | |||
BufferedImage bi = getProperties().getBrushBitmap(); | |||
return (bi == null) ? null | |||
: new TexturePaint(bi, new Rectangle(0,0,bi.getWidth(),bi.getHeight())); | |||
} | |||
@@ -244,8 +248,9 @@ public class HwmfGraphics { | |||
* Saves the current properties to the stack | |||
*/ | |||
public void saveProperties() { | |||
assert(prop != null); | |||
propStack.add(prop); | |||
prop = new HwmfDrawProperties(prop); | |||
prop = new HwmfDrawProperties(prop); | |||
} | |||
/** | |||
@@ -260,7 +265,7 @@ public class HwmfGraphics { | |||
} | |||
int stackIndex = index; | |||
if (stackIndex < 0) { | |||
int curIdx = propStack.indexOf(prop); | |||
int curIdx = propStack.indexOf(getProperties()); | |||
if (curIdx == -1) { | |||
// the current element is not pushed to the stacked, i.e. it's the last | |||
curIdx = propStack.size(); | |||
@@ -280,8 +285,8 @@ public class HwmfGraphics { | |||
* This methods gathers and sets the corresponding graphics transformations. | |||
*/ | |||
public void updateWindowMapMode() { | |||
Rectangle2D win = prop.getWindow(); | |||
HwmfMapMode mapMode = prop.getMapMode(); | |||
Rectangle2D win = getProperties().getWindow(); | |||
HwmfMapMode mapMode = getProperties().getMapMode(); | |||
graphicsCtx.setTransform(initialAT); | |||
switch (mapMode) { | |||
@@ -320,7 +325,7 @@ public class HwmfGraphics { | |||
} | |||
public void drawString(byte[] text, Rectangle2D bounds, int dx[]) { | |||
HwmfFont font = prop.getFont(); | |||
HwmfFont font = getProperties().getFont(); | |||
if (font == null || text == null || text.length == 0) { | |||
return; | |||
} | |||
@@ -386,12 +391,12 @@ public class HwmfGraphics { | |||
try { | |||
graphicsCtx.translate(bounds.getX(), bounds.getY()+fontH); | |||
graphicsCtx.rotate(angle); | |||
if (prop.getBkMode() == HwmfBkMode.OPAQUE) { | |||
if (getProperties().getBkMode() == HwmfBkMode.OPAQUE) { | |||
// TODO: validate bounds | |||
graphicsCtx.setBackground(prop.getBackgroundColor().getColor()); | |||
graphicsCtx.setBackground(getProperties().getBackgroundColor().getColor()); | |||
graphicsCtx.fill(new Rectangle2D.Double(0, 0, bounds.getWidth(), bounds.getHeight())); | |||
} | |||
graphicsCtx.setColor(prop.getTextColor().getColor()); | |||
graphicsCtx.setColor(getProperties().getTextColor().getColor()); | |||
graphicsCtx.drawString(as.getIterator(), 0, 0); // (float)bounds.getX(), (float)bounds.getY()); | |||
} finally { | |||
graphicsCtx.setTransform(at); |
@@ -42,7 +42,7 @@ public class HemfPlusExtractorTest { | |||
EmfCommentDataPlus emfPlus = getCommentRecord("SimpleEMF_windows.emf", 0); | |||
List<HemfPlusRecord> records = emfPlus.getRecords(); | |||
assertEquals(1, records.size()); | |||
assertEquals(HemfPlusRecordType.header, records.get(0).getRecordType()); | |||
assertEquals(HemfPlusRecordType.header, records.get(0).getEmfPlusRecordType()); | |||
HemfPlusHeader header = (HemfPlusHeader)records.get(0); | |||
assertEquals(240, header.getLogicalDpiX()); | |||
@@ -67,7 +67,7 @@ public class HemfPlusExtractorTest { | |||
assertEquals(expected.size(), records.size()); | |||
for (int i = 0; i < expected.size(); i++) { | |||
assertEquals(expected.get(i), records.get(i).getRecordType()); | |||
assertEquals(expected.get(i), records.get(i).getEmfPlusRecordType()); | |||
} | |||
} | |||