git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1723898 13f79535-47bb-0310-9956-ffa450edef68pull/28/head
@@ -25,14 +25,19 @@ import java.awt.geom.Rectangle2D; | |||
import java.awt.image.BufferedImage; | |||
import java.awt.image.ColorModel; | |||
import java.awt.image.WritableRaster; | |||
import java.util.List; | |||
import org.apache.poi.hwmf.record.HwmfBrushStyle; | |||
import org.apache.poi.hwmf.record.HwmfColorRef; | |||
import org.apache.poi.hwmf.record.HwmfFont; | |||
import org.apache.poi.hwmf.record.HwmfFill.WmfSetPolyfillMode.HwmfPolyfillMode; | |||
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.HwmfPalette.PaletteEntry; | |||
import org.apache.poi.hwmf.record.HwmfPenStyle; | |||
import org.apache.poi.hwmf.record.HwmfText.HwmfTextAlignment; | |||
import org.apache.poi.hwmf.record.HwmfText.HwmfTextVerticalAlignment; | |||
public class HwmfDrawProperties { | |||
private final Rectangle2D window; | |||
@@ -51,6 +56,14 @@ public class HwmfDrawProperties { | |||
private HwmfBkMode bkMode = HwmfBkMode.OPAQUE; | |||
private HwmfPolyfillMode polyfillMode = HwmfPolyfillMode.WINDING; | |||
private Shape region = null; | |||
private List<PaletteEntry> palette = null; | |||
private int paletteOffset = 0; | |||
private HwmfFont font = null; | |||
private HwmfColorRef textColor = new HwmfColorRef(Color.BLACK); | |||
private HwmfTextAlignment textAlignLatin = HwmfTextAlignment.LEFT; | |||
private HwmfTextVerticalAlignment textVAlignLatin = HwmfTextVerticalAlignment.TOP; | |||
private HwmfTextAlignment textAlignAsian = HwmfTextAlignment.RIGHT; | |||
private HwmfTextVerticalAlignment textVAlignAsian = HwmfTextVerticalAlignment.TOP; | |||
public HwmfDrawProperties() { | |||
window = new Rectangle2D.Double(0, 0, 1, 1); | |||
@@ -84,9 +97,16 @@ public class HwmfDrawProperties { | |||
} else if (other.region instanceof Area) { | |||
this.region = new Area(other.region); | |||
} | |||
this.palette = other.palette; | |||
this.paletteOffset = other.paletteOffset; | |||
this.font = other.font; | |||
this.textColor = (other.textColor == null) ? null : other.textColor.clone(); | |||
} | |||
public void setViewportExt(double width, double height) { | |||
if (viewport == null) { | |||
viewport = (Rectangle2D)window.clone(); | |||
} | |||
double x = viewport.getX(); | |||
double y = viewport.getY(); | |||
double w = (width != 0) ? width : viewport.getWidth(); | |||
@@ -244,4 +264,80 @@ public class HwmfDrawProperties { | |||
public void setRegion(Shape region) { | |||
this.region = region; | |||
} | |||
/** | |||
* Returns the current palette. | |||
* Callers may modify the palette. | |||
* | |||
* @return the current palette or null, if it hasn't been set | |||
*/ | |||
public List<PaletteEntry> getPalette() { | |||
return palette; | |||
} | |||
/** | |||
* Sets the current palette. | |||
* It's the callers duty to set a modifiable copy of the palette. | |||
* | |||
* @param palette | |||
*/ | |||
public void setPalette(List<PaletteEntry> palette) { | |||
this.palette = palette; | |||
} | |||
public int getPaletteOffset() { | |||
return paletteOffset; | |||
} | |||
public void setPaletteOffset(int paletteOffset) { | |||
this.paletteOffset = paletteOffset; | |||
} | |||
public HwmfColorRef getTextColor() { | |||
return textColor; | |||
} | |||
public void setTextColor(HwmfColorRef textColor) { | |||
this.textColor = textColor; | |||
} | |||
public HwmfFont getFont() { | |||
return font; | |||
} | |||
public void setFont(HwmfFont font) { | |||
this.font = font; | |||
} | |||
public HwmfTextAlignment getTextAlignLatin() { | |||
return textAlignLatin; | |||
} | |||
public void setTextAlignLatin(HwmfTextAlignment textAlignLatin) { | |||
this.textAlignLatin = textAlignLatin; | |||
} | |||
public HwmfTextVerticalAlignment getTextVAlignLatin() { | |||
return textVAlignLatin; | |||
} | |||
public void setTextVAlignLatin(HwmfTextVerticalAlignment textVAlignLatin) { | |||
this.textVAlignLatin = textVAlignLatin; | |||
} | |||
public HwmfTextAlignment getTextAlignAsian() { | |||
return textAlignAsian; | |||
} | |||
public void setTextAlignAsian(HwmfTextAlignment textAlignAsian) { | |||
this.textAlignAsian = textAlignAsian; | |||
} | |||
public HwmfTextVerticalAlignment getTextVAlignAsian() { | |||
return textVAlignAsian; | |||
} | |||
public void setTextVAlignAsian(HwmfTextVerticalAlignment textVAlignAsian) { | |||
this.textVAlignAsian = textVAlignAsian; | |||
} | |||
} |
@@ -25,10 +25,12 @@ import java.awt.Paint; | |||
import java.awt.Rectangle; | |||
import java.awt.Shape; | |||
import java.awt.TexturePaint; | |||
import java.awt.font.TextAttribute; | |||
import java.awt.geom.AffineTransform; | |||
import java.awt.geom.GeneralPath; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.image.BufferedImage; | |||
import java.text.AttributedString; | |||
import java.util.ArrayList; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
@@ -36,13 +38,13 @@ import java.util.ListIterator; | |||
import java.util.NoSuchElementException; | |||
import org.apache.poi.hwmf.record.HwmfBrushStyle; | |||
import org.apache.poi.hwmf.record.HwmfFont; | |||
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.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; | |||
@@ -118,7 +120,7 @@ public class HwmfGraphics { | |||
boolean dashAlt = ps.isAlternateDash(); | |||
// This value is not an integer index into the dash pattern array. | |||
// Instead, it is a floating-point value that specifies a linear distance. | |||
float dashStart = (dashAlt && dashes.length > 1) ? dashes[0] : 0; | |||
float dashStart = (dashAlt && dashes != null && dashes.length > 1) ? dashes[0] : 0; | |||
return new BasicStroke(width, cap, join, miterLimit, dashes, dashStart); | |||
} | |||
@@ -243,8 +245,8 @@ public class HwmfGraphics { | |||
/** | |||
* 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. | |||
* @param index if the index is positive, the n-th element from the start is activated. | |||
* If the index is negative, the n-th previous element relative to the current properties element is activated. | |||
*/ | |||
public void restoreProperties(int index) { | |||
if (index == 0) { | |||
@@ -253,12 +255,20 @@ public class HwmfGraphics { | |||
int stackIndex = index; | |||
if (stackIndex < 0) { | |||
int curIdx = propStack.indexOf(prop); | |||
assert (curIdx != -1); | |||
if (curIdx == -1) { | |||
// the current element is not pushed to the stacked, i.e. it's the last | |||
curIdx = propStack.size(); | |||
} | |||
stackIndex = curIdx + index; | |||
} | |||
prop = propStack.remove(stackIndex); | |||
prop = propStack.get(stackIndex); | |||
} | |||
/** | |||
* After setting various window and viewport related properties, | |||
* the underlying graphics context needs to be adapted. | |||
* This methods gathers and sets the corresponding graphics transformations. | |||
*/ | |||
public void updateWindowMapMode() { | |||
GraphicsConfiguration gc = graphicsCtx.getDeviceConfiguration(); | |||
Rectangle2D win = prop.getWindow(); | |||
@@ -268,23 +278,15 @@ public class HwmfGraphics { | |||
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()); | |||
graphicsCtx.translate(-win.getX(), -win.getY()); | |||
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()); | |||
graphicsCtx.translate(-win.getX(), -win.getY()); | |||
break; | |||
case MM_LOMETRIC: | |||
case MM_HIMETRIC: | |||
@@ -294,11 +296,48 @@ public class HwmfGraphics { | |||
// TODO: to be validated ... | |||
graphicsCtx.transform(gc.getNormalizingTransform()); | |||
graphicsCtx.scale(1./mapMode.scale, -1./mapMode.scale); | |||
graphicsCtx.translate(-bbox.getX(), -bbox.getY()); | |||
graphicsCtx.translate(-win.getX(), -win.getY()); | |||
break; | |||
case MM_TEXT: | |||
// TODO: to be validated ... | |||
break; | |||
} | |||
} | |||
public void drawString(String text, Rectangle2D bounds) { | |||
HwmfFont font = prop.getFont(); | |||
if (font == null) { | |||
return; | |||
} | |||
AttributedString as = new AttributedString(text); | |||
as.addAttribute(TextAttribute.FAMILY, font.getFacename()); | |||
// TODO: fix font height calculation | |||
as.addAttribute(TextAttribute.SIZE, Math.abs(font.getHeight())); | |||
as.addAttribute(TextAttribute.STRIKETHROUGH, font.isStrikeOut()); | |||
if (font.isUnderline()) { | |||
as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); | |||
} | |||
if (font.isItalic()) { | |||
as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); | |||
} | |||
as.addAttribute(TextAttribute.WEIGHT, font.getWeight()); | |||
double angle = Math.toRadians(-font.getEscapement()/10.); | |||
final AffineTransform at = graphicsCtx.getTransform(); | |||
try { | |||
graphicsCtx.translate(bounds.getX(), bounds.getY()); | |||
graphicsCtx.rotate(angle); | |||
if (prop.getBkMode() == HwmfBkMode.OPAQUE) { | |||
// TODO: validate bounds | |||
graphicsCtx.setBackground(prop.getBackgroundColor().getColor()); | |||
graphicsCtx.fill(bounds); | |||
} | |||
graphicsCtx.setColor(prop.getTextColor().getColor()); | |||
graphicsCtx.drawString(as.getIterator(), 0, 0); // (float)bounds.getX(), (float)bounds.getY()); | |||
} finally { | |||
graphicsCtx.setTransform(at); | |||
} | |||
} | |||
} |
@@ -17,17 +17,18 @@ | |||
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.Area; | |||
import java.awt.geom.Ellipse2D; | |||
import java.awt.geom.GeneralPath; | |||
import java.awt.geom.Line2D; | |||
import java.awt.geom.Path2D; | |||
import java.awt.geom.Point2D; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.geom.RoundRectangle2D; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.hwmf.draw.HwmfGraphics; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
@@ -114,13 +115,8 @@ public class HwmfDraw { | |||
*/ | |||
public static class WmfPolygon implements HwmfRecord { | |||
/** | |||
* A 16-bit signed integer that defines the number of points in the array. | |||
*/ | |||
private int numberofPoints; | |||
short xPoints[], yPoints[]; | |||
private Path2D poly = new Path2D.Double(); | |||
@Override | |||
public HwmfRecordType getRecordType() { | |||
return HwmfRecordType.polygon; | |||
@@ -128,15 +124,21 @@ public class HwmfDraw { | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
numberofPoints = leis.readShort(); | |||
xPoints = new short[numberofPoints]; | |||
yPoints = new short[numberofPoints]; | |||
/** | |||
* A 16-bit signed integer that defines the number of points in the array. | |||
*/ | |||
int numberofPoints = leis.readShort(); | |||
for (int i=0; i<numberofPoints; i++) { | |||
// A 16-bit signed integer that defines the horizontal (x) coordinate of the point. | |||
xPoints[i] = leis.readShort(); | |||
int x = leis.readShort(); | |||
// A 16-bit signed integer that defines the vertical (y) coordinate of the point. | |||
yPoints[i] = leis.readShort(); | |||
int y = leis.readShort(); | |||
if (i==0) { | |||
poly.moveTo(x, y); | |||
} else { | |||
poly.lineTo(x, y); | |||
} | |||
} | |||
return LittleEndianConsts.SHORT_SIZE+numberofPoints*LittleEndianConsts.INT_SIZE; | |||
@@ -144,15 +146,14 @@ public class HwmfDraw { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.fill(getShape()); | |||
Path2D p = getShape(); | |||
p.closePath(); | |||
p.setWindingRule(ctx.getProperties().getPolyfillMode().awtFlag); | |||
ctx.fill(p); | |||
} | |||
protected Polygon getShape() { | |||
Polygon polygon = new Polygon(); | |||
for(int i = 0; i < numberofPoints; i++) { | |||
polygon.addPoint(xPoints[i], yPoints[i]); | |||
} | |||
return polygon; | |||
protected Path2D getShape() { | |||
return (Path2D)poly.clone(); | |||
} | |||
} | |||
@@ -169,7 +170,9 @@ public class HwmfDraw { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.draw(getShape()); | |||
Path2D p = getShape(); | |||
p.setWindingRule(ctx.getProperties().getPolyfillMode().awtFlag); | |||
ctx.draw(p); | |||
} | |||
} | |||
@@ -234,12 +237,12 @@ public class HwmfDraw { | |||
* A 16-bit unsigned integer used to index into the WMF Object Table to get | |||
* the region to be framed. | |||
*/ | |||
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; | |||
/** | |||
* A 16-bit signed integer that defines the height, in logical units, of the | |||
* region frame. | |||
@@ -258,8 +261,8 @@ public class HwmfDraw { | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
region = leis.readUShort(); | |||
brush = leis.readUShort(); | |||
regionIndex = leis.readUShort(); | |||
brushIndex = leis.readUShort(); | |||
height = leis.readShort(); | |||
width = leis.readShort(); | |||
return 4*LittleEndianConsts.SHORT_SIZE; | |||
@@ -267,7 +270,17 @@ public class HwmfDraw { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.applyObjectTableEntry(brushIndex); | |||
ctx.applyObjectTableEntry(regionIndex); | |||
Rectangle2D inner = ctx.getProperties().getRegion().getBounds(); | |||
double x = inner.getX()-width; | |||
double y = inner.getY()-height; | |||
double w = inner.getWidth()+2*width; | |||
double h = inner.getHeight()+2*height; | |||
Rectangle2D outer = new Rectangle2D.Double(x,y,w,h); | |||
Area frame = new Area(outer); | |||
frame.subtract(new Area(inner)); | |||
ctx.fill(frame); | |||
} | |||
} | |||
@@ -278,28 +291,8 @@ public class HwmfDraw { | |||
*/ | |||
public static class WmfPolyPolygon implements HwmfRecord { | |||
/** | |||
* A 16-bit unsigned integer that defines the number of polygons in the object. | |||
*/ | |||
private int numberOfPolygons; | |||
/** | |||
* A NumberOfPolygons array of 16-bit unsigned integers that define the number of | |||
* points for each polygon in the object. | |||
*/ | |||
private int pointsPerPolygon[]; | |||
/** | |||
* An array of 16-bit unsigned integers that define the coordinates of the polygons. | |||
*/ | |||
private int xPoints[][]; | |||
/** | |||
* An array of 16-bit unsigned integers that define the coordinates of the polygons. | |||
*/ | |||
private int yPoints[][]; | |||
private List<Path2D> polyList = new ArrayList<Path2D>(); | |||
@Override | |||
public HwmfRecordType getRecordType() { | |||
return HwmfRecordType.polyPolygon; | |||
@@ -308,10 +301,15 @@ public class HwmfDraw { | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
// see http://secunia.com/gfx/pdf/SA31675_BA.pdf ;) | |||
numberOfPolygons = leis.readUShort(); | |||
pointsPerPolygon = new int[numberOfPolygons]; | |||
xPoints = new int[numberOfPolygons][]; | |||
yPoints = new int[numberOfPolygons][]; | |||
/** | |||
* A 16-bit unsigned integer that defines the number of polygons in the object. | |||
*/ | |||
int numberOfPolygons = leis.readUShort(); | |||
/** | |||
* A NumberOfPolygons array of 16-bit unsigned integers that define the number of | |||
* points for each polygon in the object. | |||
*/ | |||
int[] pointsPerPolygon = new int[numberOfPolygons]; | |||
int size = LittleEndianConsts.SHORT_SIZE; | |||
@@ -320,16 +318,23 @@ public class HwmfDraw { | |||
size += LittleEndianConsts.SHORT_SIZE; | |||
} | |||
for (int i=0; i<numberOfPolygons; i++) { | |||
xPoints[i] = new int[pointsPerPolygon[i]]; | |||
yPoints[i] = new int[pointsPerPolygon[i]]; | |||
for (int j=0; j<pointsPerPolygon[i]; j++) { | |||
xPoints[i][j] = leis.readUShort(); | |||
yPoints[i][j] = leis.readUShort(); | |||
for (int nPoints : pointsPerPolygon) { | |||
/** | |||
* An array of 16-bit unsigned integers that define the coordinates of the polygons. | |||
*/ | |||
Path2D poly = new Path2D.Double(); | |||
for (int i=0; i<nPoints; i++) { | |||
int x = leis.readUShort(); | |||
int y = leis.readUShort(); | |||
size += 2*LittleEndianConsts.SHORT_SIZE; | |||
if (i == 0) { | |||
poly.moveTo(x, y); | |||
} else { | |||
poly.lineTo(x, y); | |||
} | |||
} | |||
poly.closePath(); | |||
polyList.add(poly); | |||
} | |||
return size; | |||
@@ -337,7 +342,14 @@ public class HwmfDraw { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
int windingRule = ctx.getProperties().getPolyfillMode().awtFlag; | |||
Area area = new Area(); | |||
for (Path2D poly : polyList) { | |||
Path2D p = (Path2D)poly.clone(); | |||
p.setWindingRule(windingRule); | |||
area.add(new Area(p)); | |||
} | |||
ctx.draw(area); | |||
} | |||
} | |||
@@ -38,6 +38,42 @@ public class HwmfFill { | |||
BufferedImage getImage(); | |||
} | |||
/** | |||
* The ColorUsage Enumeration (a 16-bit unsigned integer) specifies whether a color table | |||
* exists in a device-independent bitmap (DIB) and how to interpret its values, | |||
* i.e. if contains explicit RGB values or indexes into a palette. | |||
*/ | |||
public enum ColorUsage { | |||
/** | |||
* The color table contains RGB values | |||
*/ | |||
DIB_RGB_COLORS(0x0000), | |||
/** | |||
* The color table contains 16-bit indices into the current logical palette in | |||
* the playback device context. | |||
*/ | |||
DIB_PAL_COLORS(0x0001), | |||
/** | |||
* No color table exists. The pixels in the DIB are indices into the current | |||
* logical palette in the playback device context. | |||
*/ | |||
DIB_PAL_INDICES(0x0002) | |||
; | |||
int flag; | |||
ColorUsage(int flag) { | |||
this.flag = flag; | |||
} | |||
static ColorUsage valueOf(int flag) { | |||
for (ColorUsage bs : values()) { | |||
if (bs.flag == flag) return bs; | |||
} | |||
return null; | |||
} | |||
} | |||
/** | |||
* The META_FILLREGION record fills a region using a specified brush. | |||
@@ -482,12 +518,8 @@ public class HwmfFill { | |||
/** | |||
* A 16-bit unsigned integer that defines whether the Colors field of the | |||
* DIB contains explicit RGB values or indexes into a palette. | |||
* This value MUST be in the ColorUsage Enumeration: | |||
* DIB_RGB_COLORS = 0x0000, | |||
* DIB_PAL_COLORS = 0x0001, | |||
* DIB_PAL_INDICES = 0x0002 | |||
*/ | |||
private int colorUsage; | |||
private ColorUsage colorUsage; | |||
/** | |||
* A 16-bit signed integer that defines the height, in logical units, of the | |||
* source rectangle. | |||
@@ -548,7 +580,7 @@ public class HwmfFill { | |||
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex); | |||
assert(rasterOpCode == rasterOperation.opCode); | |||
colorUsage = leis.readUShort(); | |||
colorUsage = ColorUsage.valueOf(leis.readUShort()); | |||
srcHeight = leis.readShort(); | |||
srcWidth = leis.readShort(); | |||
ySrc = leis.readShort(); | |||
@@ -679,12 +711,8 @@ public class HwmfFill { | |||
/** | |||
* A 16-bit unsigned integer that defines whether the Colors field of the | |||
* DIB contains explicit RGB values or indexes into a palette. | |||
* This MUST be one of the values in the ColorUsage Enumeration: | |||
* DIB_RGB_COLORS = 0x0000, | |||
* DIB_PAL_COLORS = 0x0001, | |||
* DIB_PAL_INDICES = 0x0002 | |||
*/ | |||
private int colorUsage; | |||
private ColorUsage colorUsage; | |||
/** | |||
* A 16-bit unsigned integer that defines the number of scan lines in the source. | |||
*/ | |||
@@ -736,7 +764,7 @@ public class HwmfFill { | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
colorUsage = leis.readUShort(); | |||
colorUsage = ColorUsage.valueOf(leis.readUShort()); | |||
scanCount = leis.readUShort(); | |||
startScan = leis.readUShort(); | |||
yDib = leis.readUShort(); |
@@ -497,4 +497,64 @@ public class HwmfFont { | |||
return 5*LittleEndianConsts.SHORT_SIZE+8*LittleEndianConsts.BYTE_SIZE+readBytes; | |||
} | |||
public int getHeight() { | |||
return height; | |||
} | |||
public int getWidth() { | |||
return width; | |||
} | |||
public int getEscapement() { | |||
return escapement; | |||
} | |||
public int getOrientation() { | |||
return orientation; | |||
} | |||
public int getWeight() { | |||
return weight; | |||
} | |||
public boolean isItalic() { | |||
return italic; | |||
} | |||
public boolean isUnderline() { | |||
return underline; | |||
} | |||
public boolean isStrikeOut() { | |||
return strikeOut; | |||
} | |||
public WmfCharset getCharSet() { | |||
return charSet; | |||
} | |||
public WmfOutPrecision getOutPrecision() { | |||
return outPrecision; | |||
} | |||
public WmfClipPrecision getClipPrecision() { | |||
return clipPrecision; | |||
} | |||
public WmfFontQuality getQuality() { | |||
return quality; | |||
} | |||
public WmfFontFamilyClass getFamily() { | |||
return family; | |||
} | |||
public WmfFontPitch getPitch() { | |||
return pitch; | |||
} | |||
public String getFacename() { | |||
return facename; | |||
} | |||
} |
@@ -22,6 +22,7 @@ import java.io.IOException; | |||
import org.apache.poi.hwmf.draw.HwmfDrawProperties; | |||
import org.apache.poi.hwmf.draw.HwmfGraphics; | |||
import org.apache.poi.hwmf.record.HwmfFill.ColorUsage; | |||
import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
import org.apache.poi.util.LittleEndianInputStream; | |||
@@ -349,12 +350,9 @@ public class HwmfMisc { | |||
* If the Style field specifies BS_PATTERN, a ColorUsage value of DIB_RGB_COLORS MUST be | |||
* used regardless of the contents of this field. | |||
* | |||
* If the Style field specified anything but BS_PATTERN, this field MUST be one of the values: | |||
* DIB_RGB_COLORS = 0x0000, | |||
* DIB_PAL_COLORS = 0x0001, | |||
* DIB_PAL_INDICES = 0x0002 | |||
* If the Style field specified anything but BS_PATTERN, this field MUST be one of the ColorUsage values. | |||
*/ | |||
private int colorUsage; | |||
private ColorUsage colorUsage; | |||
private HwmfBitmapDib patternDib; | |||
private HwmfBitmap16 pattern16; | |||
@@ -367,7 +365,7 @@ public class HwmfMisc { | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
style = HwmfBrushStyle.valueOf(leis.readUShort()); | |||
colorUsage = leis.readUShort(); | |||
colorUsage = ColorUsage.valueOf(leis.readUShort()); | |||
int size = 2*LittleEndianConsts.SHORT_SIZE; | |||
switch (style) { | |||
case BS_SOLID: |
@@ -19,106 +19,121 @@ package org.apache.poi.hwmf.record; | |||
import java.awt.Color; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.hwmf.draw.HwmfDrawProperties; | |||
import org.apache.poi.hwmf.draw.HwmfGraphics; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
import org.apache.poi.util.LittleEndianInputStream; | |||
public class HwmfPalette { | |||
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; | |||
} | |||
private static final BitField PC_RESERVED = BitFieldFactory.getInstance(0x01); | |||
private static final BitField PC_EXPLICIT = BitFieldFactory.getInstance(0x02); | |||
private static final BitField PC_NOCOLLAPSE = BitFieldFactory.getInstance(0x04); | |||
static PaletteEntryFlag valueOf(int flag) { | |||
for (PaletteEntryFlag pef : values()) { | |||
if (pef.flag == flag) return pef; | |||
} | |||
return null; | |||
} | |||
private int values; | |||
private Color colorRef; | |||
private PaletteEntry() { | |||
this.values = PC_RESERVED.set(0); | |||
this.colorRef = Color.BLACK; | |||
} | |||
// 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. | |||
// 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. | |||
// Red (1 byte): An 8-bit unsigned integer that defines the red intensity value for the palette entry. | |||
private PaletteEntryFlag values; | |||
private Color colorRef; | |||
private PaletteEntry(PaletteEntry other) { | |||
this.values = other.values; | |||
this.colorRef = other.colorRef; | |||
} | |||
public int init(LittleEndianInputStream leis) throws IOException { | |||
values = PaletteEntryFlag.valueOf(leis.readUByte()); | |||
// 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. | |||
values = leis.readUByte(); | |||
// Blue (1 byte): An 8-bit unsigned integer that defines the blue intensity value for the palette entry. | |||
int blue = leis.readUByte(); | |||
// Green (1 byte): An 8-bit unsigned integer that defines the green intensity value for the palette entry. | |||
int green = leis.readUByte(); | |||
// Red (1 byte): An 8-bit unsigned integer that defines the red intensity value for the palette entry. | |||
int red = leis.readUByte(); | |||
colorRef = new Color(red, green, blue); | |||
return 4*LittleEndianConsts.BYTE_SIZE; | |||
} | |||
/** | |||
* 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. | |||
*/ | |||
public boolean isReserved() { | |||
return PC_RESERVED.isSet(values); | |||
} | |||
/** | |||
* 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. | |||
*/ | |||
public boolean isExplicit() { | |||
return PC_EXPLICIT.isSet(values); | |||
} | |||
/** | |||
* 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. | |||
*/ | |||
public boolean isNoCollapse() { | |||
return PC_NOCOLLAPSE.isSet(values); | |||
} | |||
} | |||
public static abstract class WmfPaletteParent implements HwmfRecord { | |||
/** | |||
* Start (2 bytes): A 16-bit unsigned integer that defines the offset into the Palette Object when | |||
* used with the META_SETPALENTRIES and META_ANIMATEPALETTE record types. | |||
* When used with META_CREATEPALETTE, it MUST be 0x0300 | |||
*/ | |||
private int start; | |||
/** | |||
* NumberOfEntries (2 bytes): A 16-bit unsigned integer that defines the number of objects in | |||
* aPaletteEntries. | |||
*/ | |||
private int numberOfEntries; | |||
private PaletteEntry entries[]; | |||
private List<PaletteEntry> palette = new ArrayList<PaletteEntry>(); | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
start = leis.readUShort(); | |||
numberOfEntries = leis.readUShort(); | |||
/** | |||
* NumberOfEntries (2 bytes): A 16-bit unsigned integer that defines the number of objects in | |||
* aPaletteEntries. | |||
*/ | |||
int numberOfEntries = leis.readUShort(); | |||
int size = 2*LittleEndianConsts.SHORT_SIZE; | |||
entries = new PaletteEntry[numberOfEntries]; | |||
for (int i=0; i<numberOfEntries; i++) { | |||
entries[i] = new PaletteEntry(); | |||
size += entries[i].init(leis); | |||
PaletteEntry pe = new PaletteEntry(); | |||
size += pe.init(leis); | |||
palette.add(pe); | |||
} | |||
return size; | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
protected List<PaletteEntry> getPaletteCopy() { | |||
List<PaletteEntry> newPalette = new ArrayList<PaletteEntry>(); | |||
for (PaletteEntry et : palette) { | |||
newPalette.add(new PaletteEntry(et)); | |||
} | |||
return newPalette; | |||
} | |||
protected int getPaletteStart() { | |||
return start; | |||
} | |||
} | |||
/** | |||
* The META_CREATEPALETTE record creates a Palette Object | |||
*/ | |||
@@ -132,10 +147,10 @@ public class HwmfPalette { | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.addObjectTableEntry(this); | |||
} | |||
@Override | |||
public void applyObject(HwmfGraphics ctx) { | |||
ctx.getProperties().setPalette(getPaletteCopy()); | |||
} | |||
} | |||
@@ -151,35 +166,62 @@ public class HwmfPalette { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
HwmfDrawProperties props = ctx.getProperties(); | |||
List<PaletteEntry> palette = props.getPalette(); | |||
if (palette == null) { | |||
palette = new ArrayList<PaletteEntry>(); | |||
} | |||
int start = getPaletteStart(); | |||
for (int i=palette.size(); i<start; i++) { | |||
palette.add(new PaletteEntry()); | |||
} | |||
int index = start; | |||
for (PaletteEntry palCopy : getPaletteCopy()) { | |||
if (palette.size() <= index) { | |||
palette.add(palCopy); | |||
} else { | |||
palette.set(index, palCopy); | |||
} | |||
index++; | |||
} | |||
props.setPalette(palette); | |||
} | |||
} | |||
/** | |||
* The META_RESIZEPALETTE record redefines the size of the logical palette that is defined in the | |||
* playback device context. | |||
*/ | |||
public static class WmfResizePalette implements HwmfRecord { | |||
/** | |||
* A 16-bit unsigned integer that defines the number of entries in | |||
* A 16-bit unsigned integer that defines the number of entries in | |||
* the logical palette. | |||
*/ | |||
int numberOfEntries; | |||
@Override | |||
public HwmfRecordType getRecordType() { | |||
return HwmfRecordType.resizePalette; | |||
} | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
numberOfEntries = leis.readUShort(); | |||
return LittleEndianConsts.SHORT_SIZE; | |||
} | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
HwmfDrawProperties props = ctx.getProperties(); | |||
List<PaletteEntry> palette = props.getPalette(); | |||
if (palette == null) { | |||
palette = new ArrayList<PaletteEntry>(); | |||
} | |||
for (int i=palette.size(); i<numberOfEntries; i++) { | |||
palette.add(new PaletteEntry()); | |||
} | |||
palette = palette.subList(0, numberOfEntries); | |||
props.setPalette(palette); | |||
} | |||
} | |||
@@ -191,22 +233,22 @@ public class HwmfPalette { | |||
* A 16-bit unsigned integer used to index into the WMF Object Table to get | |||
* the Palette Object to be selected. | |||
*/ | |||
private int palette; | |||
private int paletteIndex; | |||
@Override | |||
public HwmfRecordType getRecordType() { | |||
return HwmfRecordType.selectPalette; | |||
} | |||
@Override | |||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { | |||
palette = leis.readUShort(); | |||
paletteIndex = leis.readUShort(); | |||
return LittleEndianConsts.SHORT_SIZE; | |||
} | |||
} | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.applyObjectTableEntry(paletteIndex); | |||
} | |||
} | |||
@@ -234,7 +276,7 @@ public class HwmfPalette { | |||
/** | |||
* The META_ANIMATEPALETTE record redefines entries in the logical palette that | |||
* is defined in the playback device context with the specified Palette object | |||
* | |||
* | |||
* The logical palette that is specified by the Palette object in this record is the | |||
* source of the palette changes, and the logical palette that is currently selected | |||
* into the playback device context is the destination. Entries in the destination | |||
@@ -251,7 +293,28 @@ public class HwmfPalette { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
HwmfDrawProperties props = ctx.getProperties(); | |||
List<PaletteEntry> dest = props.getPalette(); | |||
List<PaletteEntry> src = getPaletteCopy(); | |||
int start = getPaletteStart(); | |||
if (dest == null) { | |||
dest = new ArrayList<PaletteEntry>(); | |||
} | |||
for (int i=dest.size(); i<start; i++) { | |||
dest.add(new PaletteEntry()); | |||
} | |||
for (int i=0; i<src.size(); i++) { | |||
PaletteEntry pe = src.get(i); | |||
if (dest.size() <= start+i) { | |||
dest.add(pe); | |||
} else { | |||
PaletteEntry peDst = dest.get(start+i); | |||
if (peDst.isReserved()) { | |||
dest.set(start+i, pe); | |||
} | |||
} | |||
} | |||
props.setPalette(dest); | |||
} | |||
} | |||
} |
@@ -86,7 +86,7 @@ public class HwmfPenStyle implements Cloneable { | |||
/** | |||
* The pen is solid. | |||
*/ | |||
SOLID(0x0000, 10), | |||
SOLID(0x0000, null), | |||
/** | |||
* The pen is dashed. (-----) | |||
*/ | |||
@@ -106,19 +106,19 @@ public class HwmfPenStyle implements Cloneable { | |||
/** | |||
* The pen is invisible. | |||
*/ | |||
NULL(0x0005), | |||
NULL(0x0005, null), | |||
/** | |||
* The pen is solid. When this pen is used in any drawing record that takes a | |||
* bounding rectangle, the dimensions of the figure are shrunk so that it fits | |||
* entirely in the bounding rectangle, taking into account the width of the pen. | |||
*/ | |||
INSIDEFRAME(0x0006, 10), | |||
INSIDEFRAME(0x0006, null), | |||
/** | |||
* The pen uses a styling array supplied by the user. | |||
* (this is currently not supported and drawn as solid ... no idea where the user | |||
* styling is supposed to come from ...) | |||
*/ | |||
USERSTYLE(0x0007, 10); | |||
USERSTYLE(0x0007, null); | |||
public int wmfFlag; |
@@ -27,6 +27,7 @@ public class HwmfPlaceableHeader { | |||
public static int WMF_HEADER_MAGIC = 0x9AC6CDD7; | |||
final Rectangle2D bounds; | |||
final int unitsPerInch; | |||
protected HwmfPlaceableHeader(LittleEndianInputStream leis) throws IOException { | |||
/* | |||
@@ -53,7 +54,7 @@ public class HwmfPlaceableHeader { | |||
* Thus, a value of 720 specifies that the image SHOULD be rendered at twice its normal size, | |||
* and a value of 2880 specifies that the image SHOULD be rendered at half its normal size. | |||
*/ | |||
int inch = leis.readShort(); | |||
unitsPerInch = leis.readShort(); | |||
/* | |||
* Reserved (4 bytes): A field that is not used and MUST be set to 0x00000000. | |||
@@ -82,4 +83,8 @@ public class HwmfPlaceableHeader { | |||
public Rectangle2D getBounds() { | |||
return bounds; | |||
} | |||
public int getUnitsPerInch() { | |||
return unitsPerInch; | |||
} | |||
} |
@@ -17,10 +17,15 @@ | |||
package org.apache.poi.hwmf.record; | |||
import java.awt.geom.Rectangle2D; | |||
import java.io.IOException; | |||
import java.text.AttributedString; | |||
import org.apache.poi.hwmf.draw.HwmfDrawProperties; | |||
import org.apache.poi.hwmf.draw.HwmfGraphics; | |||
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
import org.apache.poi.util.LittleEndianInputStream; | |||
import org.apache.poi.util.LocaleUtil; | |||
@@ -80,7 +85,7 @@ public class HwmfText { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
ctx.getProperties().setTextColor(colorRef); | |||
} | |||
} | |||
@@ -168,7 +173,8 @@ public class HwmfText { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
Rectangle2D bounds = new Rectangle2D.Double(xStart, yStart, 0, 0); | |||
ctx.drawString(text, bounds); | |||
} | |||
} | |||
@@ -304,74 +310,125 @@ public class HwmfText { | |||
} | |||
} | |||
public enum HwmfTextAlignment { | |||
LEFT, | |||
RIGHT, | |||
CENTER; | |||
} | |||
public enum HwmfTextVerticalAlignment { | |||
TOP, | |||
BOTTOM, | |||
BASELINE; | |||
} | |||
/** | |||
* The META_SETTEXTALIGN record defines text-alignment values in the playback device context. | |||
*/ | |||
public static class WmfSetTextAlign implements HwmfRecord { | |||
/** | |||
* A 16-bit unsigned integer that defines text alignment. | |||
* This value MUST be a combination of one or more TextAlignmentMode Flags | |||
* for text with a horizontal baseline, and VerticalTextAlignmentMode Flags | |||
* for text with a vertical baseline. | |||
* | |||
* TextAlignmentMode Flags: | |||
* TA_NOUPDATECP (0x0000): | |||
// *********************************************************************************** | |||
// TextAlignmentMode Flags: | |||
// *********************************************************************************** | |||
/** | |||
* The drawing position in the playback device context MUST NOT be updated after each | |||
* text output call. The reference point MUST be passed to the text output function. | |||
* | |||
* TA_LEFT (0x0000): | |||
*/ | |||
@SuppressWarnings("unused") | |||
private static final BitField TA_NOUPDATECP = BitFieldFactory.getInstance(0x0000); | |||
/** | |||
* The reference point MUST be on the left edge of the bounding rectangle. | |||
* | |||
* TA_TOP (0x0000): | |||
*/ | |||
@SuppressWarnings("unused") | |||
private static final BitField TA_LEFT = BitFieldFactory.getInstance(0x0000); | |||
/** | |||
* The reference point MUST be on the top edge of the bounding rectangle. | |||
* | |||
* TA_UPDATECP (0x0001): | |||
*/ | |||
@SuppressWarnings("unused") | |||
private static final BitField TA_TOP = BitFieldFactory.getInstance(0x0000); | |||
/** | |||
* The drawing position in the playback device context MUST be updated after each text | |||
* output call. It MUST be used as the reference point. | |||
* | |||
* TA_RIGHT (0x0002): | |||
*/ | |||
@SuppressWarnings("unused") | |||
private static final BitField TA_UPDATECP = BitFieldFactory.getInstance(0x0001); | |||
/** | |||
* The reference point MUST be on the right edge of the bounding rectangle. | |||
* | |||
* TA_CENTER (0x0006): | |||
*/ | |||
private static final BitField TA_RIGHT = BitFieldFactory.getInstance(0x0002); | |||
/** | |||
* The reference point MUST be aligned horizontally with the center of the bounding | |||
* rectangle. | |||
* | |||
* TA_BOTTOM (0x0008): | |||
*/ | |||
private static final BitField TA_CENTER = BitFieldFactory.getInstance(0x0006); | |||
/** | |||
* The reference point MUST be on the bottom edge of the bounding rectangle. | |||
* | |||
* TA_BASELINE (0x0018): | |||
*/ | |||
private static final BitField TA_BOTTOM = BitFieldFactory.getInstance(0x0008); | |||
/** | |||
* The reference point MUST be on the baseline of the text. | |||
* | |||
* TA_RTLREADING (0x0100): | |||
*/ | |||
private static final BitField TA_BASELINE = BitFieldFactory.getInstance(0x0018); | |||
/** | |||
* The text MUST be laid out in right-to-left reading order, instead of the default | |||
* left-toright order. This SHOULD be applied only when the font that is defined in the | |||
* left-to-right order. This SHOULD be applied only when the font that is defined in the | |||
* playback device context is either Hebrew or Arabic. | |||
* | |||
* | |||
* VerticalTextAlignmentMode Flags (e.g. for Kanji fonts) | |||
* VTA_TOP (0x0000): | |||
*/ | |||
@SuppressWarnings("unused") | |||
private static final BitField TA_RTLREADING = BitFieldFactory.getInstance(0x0100); | |||
// *********************************************************************************** | |||
// VerticalTextAlignmentMode Flags (e.g. for Kanji fonts) | |||
// *********************************************************************************** | |||
/** | |||
* The reference point MUST be on the top edge of the bounding rectangle. | |||
* | |||
* VTA_RIGHT (0x0000): | |||
*/ | |||
@SuppressWarnings("unused") | |||
private static final BitField VTA_TOP = BitFieldFactory.getInstance(0x0000); | |||
/** | |||
* The reference point MUST be on the right edge of the bounding rectangle. | |||
* | |||
* VTA_BOTTOM (0x0002): | |||
*/ | |||
@SuppressWarnings("unused") | |||
private static final BitField VTA_RIGHT = BitFieldFactory.getInstance(0x0000); | |||
/** | |||
* The reference point MUST be on the bottom edge of the bounding rectangle. | |||
* | |||
* VTA_CENTER (0x0006): | |||
*/ | |||
private static final BitField VTA_BOTTOM = BitFieldFactory.getInstance(0x0002); | |||
/** | |||
* The reference point MUST be aligned vertically with the center of the bounding | |||
* rectangle. | |||
* | |||
* VTA_LEFT (0x0008): | |||
*/ | |||
private static final BitField VTA_CENTER = BitFieldFactory.getInstance(0x0006); | |||
/** | |||
* The reference point MUST be on the left edge of the bounding rectangle. | |||
* | |||
* VTA_BASELINE (0x0018): | |||
*/ | |||
private static final BitField VTA_LEFT = BitFieldFactory.getInstance(0x0008); | |||
/** | |||
* The reference point MUST be on the baseline of the text. | |||
*/ | |||
private static final BitField VTA_BASELINE = BitFieldFactory.getInstance(0x0018); | |||
/** | |||
* A 16-bit unsigned integer that defines text alignment. | |||
* This value MUST be a combination of one or more TextAlignmentMode Flags | |||
* for text with a horizontal baseline, and VerticalTextAlignmentMode Flags | |||
* for text with a vertical baseline. | |||
*/ | |||
private int textAlignmentMode; | |||
@Override | |||
@@ -387,7 +444,38 @@ public class HwmfText { | |||
@Override | |||
public void draw(HwmfGraphics ctx) { | |||
HwmfDrawProperties props = ctx.getProperties(); | |||
if (TA_CENTER.isSet(textAlignmentMode)) { | |||
props.setTextAlignLatin(HwmfTextAlignment.CENTER); | |||
} else if (TA_RIGHT.isSet(textAlignmentMode)) { | |||
props.setTextAlignLatin(HwmfTextAlignment.RIGHT); | |||
} else { | |||
props.setTextAlignLatin(HwmfTextAlignment.LEFT); | |||
} | |||
if (VTA_CENTER.isSet(textAlignmentMode)) { | |||
props.setTextAlignAsian(HwmfTextAlignment.CENTER); | |||
} else if (VTA_LEFT.isSet(textAlignmentMode)) { | |||
props.setTextAlignAsian(HwmfTextAlignment.LEFT); | |||
} else { | |||
props.setTextAlignAsian(HwmfTextAlignment.RIGHT); | |||
} | |||
if (TA_BASELINE.isSet(textAlignmentMode)) { | |||
props.setTextVAlignLatin(HwmfTextVerticalAlignment.BASELINE); | |||
} else if (TA_BOTTOM.isSet(textAlignmentMode)) { | |||
props.setTextVAlignLatin(HwmfTextVerticalAlignment.BOTTOM); | |||
} else { | |||
props.setTextVAlignLatin(HwmfTextVerticalAlignment.TOP); | |||
} | |||
if (VTA_BASELINE.isSet(textAlignmentMode)) { | |||
props.setTextVAlignAsian(HwmfTextVerticalAlignment.BASELINE); | |||
} else if (VTA_BOTTOM.isSet(textAlignmentMode)) { | |||
props.setTextVAlignAsian(HwmfTextVerticalAlignment.BOTTOM); | |||
} else { | |||
props.setTextVAlignAsian(HwmfTextVerticalAlignment.TOP); | |||
} | |||
} | |||
} | |||
@@ -412,7 +500,7 @@ public class HwmfText { | |||
@Override | |||
public void applyObject(HwmfGraphics ctx) { | |||
ctx.getProperties().setFont(font); | |||
} | |||
} | |||
} |
@@ -17,9 +17,12 @@ | |||
package org.apache.poi.hwmf.usermodel; | |||
import java.awt.Dimension; | |||
import java.awt.Graphics2D; | |||
import java.awt.GraphicsConfiguration; | |||
import java.awt.geom.AffineTransform; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.geom.Rectangle2D.Double; | |||
import java.io.BufferedInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
@@ -35,6 +38,7 @@ 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; | |||
import org.apache.poi.util.Units; | |||
public class HwmfPicture { | |||
final List<HwmfRecord> records = new ArrayList<HwmfRecord>(); | |||
@@ -85,9 +89,23 @@ public class HwmfPicture { | |||
} | |||
public void draw(Graphics2D ctx) { | |||
Dimension dim = getSize(); | |||
int width = Units.pointsToPixel(dim.getWidth()); | |||
// keep aspect ratio for height | |||
int height = Units.pointsToPixel(dim.getHeight()); | |||
Rectangle2D bounds = new Rectangle2D.Double(0,0,width,height); | |||
draw(ctx, bounds); | |||
} | |||
public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) { | |||
AffineTransform at = ctx.getTransform(); | |||
try { | |||
HwmfGraphics g = new HwmfGraphics(ctx, getBounds()); | |||
Rectangle2D wmfBounds = getBounds(); | |||
// scale output bounds to image bounds | |||
ctx.scale(graphicsBounds.getWidth()/wmfBounds.getWidth(), graphicsBounds.getHeight()/wmfBounds.getHeight()); | |||
ctx.translate(-wmfBounds.getX(), -wmfBounds.getY()); | |||
HwmfGraphics g = new HwmfGraphics(ctx, wmfBounds); | |||
for (HwmfRecord r : records) { | |||
r.draw(g); | |||
} | |||
@@ -131,4 +149,18 @@ public class HwmfPicture { | |||
public HwmfHeader getHeader() { | |||
return header; | |||
} | |||
/** | |||
* Return the image size in points | |||
* | |||
* @return the image size in points | |||
*/ | |||
public Dimension getSize() { | |||
double inch = (placeableHeader == null) ? 1440 : placeableHeader.getUnitsPerInch(); | |||
Rectangle2D bounds = getBounds(); | |||
//coefficient to translate from WMF dpi to 72dpi | |||
double coeff = Units.POINT_DPI/inch; | |||
return new Dimension((int)Math.round(bounds.getWidth()*coeff), (int)Math.round(bounds.getHeight()*coeff)); | |||
} | |||
} |
@@ -19,9 +19,9 @@ package org.apache.poi.hwmf; | |||
import static org.junit.Assert.assertEquals; | |||
import java.awt.Dimension; | |||
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; | |||
@@ -45,6 +45,7 @@ import org.apache.poi.sl.usermodel.PictureData; | |||
import org.apache.poi.sl.usermodel.PictureData.PictureType; | |||
import org.apache.poi.sl.usermodel.SlideShow; | |||
import org.apache.poi.sl.usermodel.SlideShowFactory; | |||
import org.apache.poi.util.Units; | |||
import org.junit.Ignore; | |||
import org.junit.Test; | |||
@@ -60,17 +61,18 @@ public class TestHwmfParsing { | |||
} | |||
@Test | |||
@Ignore | |||
@Ignore("This is work-in-progress and not a real unit test ...") | |||
public void paint() throws IOException { | |||
File f = POIDataSamples.getSlideShowInstance().getFile("santa.wmf"); | |||
// File f = new File("E:\\project\\poi\\misc\\govdocs-ppt", "000133-0001.wmf"); | |||
FileInputStream fis = new FileInputStream(f); | |||
HwmfPicture wmf = new HwmfPicture(fis); | |||
fis.close(); | |||
Rectangle2D bounds = wmf.getBounds(); | |||
int width = 300; | |||
Dimension dim = wmf.getSize(); | |||
int width = Units.pointsToPixel(dim.getWidth()); | |||
// keep aspect ratio for height | |||
int height = (int)(width*bounds.getHeight()/bounds.getWidth()); | |||
int height = Units.pointsToPixel(dim.getHeight()); | |||
BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | |||
Graphics2D g = bufImg.createGraphics(); | |||
@@ -87,7 +89,7 @@ public class TestHwmfParsing { | |||
} | |||
@Test | |||
@Ignore | |||
@Ignore("This is work-in-progress and not a real unit test ...") | |||
public void fetchWmfFromGovdocs() throws IOException { | |||
URL url = new URL("http://digitalcorpora.org/corpora/files/govdocs1/by_type/ppt.zip"); | |||
File outdir = new File("build/ppt"); | |||
@@ -117,12 +119,16 @@ public class TestHwmfParsing { | |||
} | |||
} | |||
} | |||
@Test | |||
@Ignore | |||
@Ignore("This is work-in-progress and not a real unit test ...") | |||
public void parseWmfs() throws IOException { | |||
// parse and render the extracted wmfs from the fetchWmfFromGovdocs step | |||
boolean outputFiles = false; | |||
File indir = new File("build/ppt"), outdir = indir; | |||
boolean renderWmf = true; | |||
File indir = new File("E:\\project\\poi\\misc\\govdocs-ppt"); | |||
File outdir = new File("build/wmf"); | |||
outdir.mkdirs(); | |||
final String startFile = ""; | |||
File files[] = indir.listFiles(new FileFilter() { | |||
boolean foundStartFile = false; | |||
@@ -149,6 +155,26 @@ public class TestHwmfParsing { | |||
bmpIndex++; | |||
} | |||
} | |||
if (renderWmf) { | |||
Dimension dim = wmf.getSize(); | |||
int width = Units.pointsToPixel(dim.getWidth()); | |||
// keep aspect ratio for height | |||
int height = Units.pointsToPixel(dim.getHeight()); | |||
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); | |||
wmf.draw(g); | |||
g.dispose(); | |||
ImageIO.write(bufImg, "PNG", new File(outdir, basename+".png")); | |||
} | |||
} catch (Exception e) { | |||
System.out.println(f.getName()+" ignored."); | |||
} |