mirror of https://github.com/apache/poi.git
WMF fixes
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1723898 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
7de587053e
commit
2962d7e1ba
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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 int values;
|
||||
private Color colorRef;
|
||||
|
||||
|
||||
private PaletteEntry() {
|
||||
this.values = PC_RESERVED.set(0);
|
||||
this.colorRef = Color.BLACK;
|
||||
}
|
||||
|
||||
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,73 +310,124 @@ 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 {
|
||||
|
||||
// ***********************************************************************************
|
||||
// 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.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static final BitField TA_NOUPDATECP = BitFieldFactory.getInstance(0x0000);
|
||||
|
||||
/**
|
||||
* The reference point MUST be on the left edge of the bounding rectangle.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static final BitField TA_LEFT = BitFieldFactory.getInstance(0x0000);
|
||||
|
||||
/**
|
||||
* The reference point MUST be on the top edge of the bounding rectangle.
|
||||
*/
|
||||
@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.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static final BitField TA_UPDATECP = BitFieldFactory.getInstance(0x0001);
|
||||
|
||||
/**
|
||||
* The reference point MUST be on the right edge of the bounding rectangle.
|
||||
*/
|
||||
private static final BitField TA_RIGHT = BitFieldFactory.getInstance(0x0002);
|
||||
|
||||
/**
|
||||
* The reference point MUST be aligned horizontally with the center of the bounding
|
||||
* rectangle.
|
||||
*/
|
||||
private static final BitField TA_CENTER = BitFieldFactory.getInstance(0x0006);
|
||||
|
||||
/**
|
||||
* The reference point MUST be on the bottom edge of the bounding rectangle.
|
||||
*/
|
||||
private static final BitField TA_BOTTOM = BitFieldFactory.getInstance(0x0008);
|
||||
|
||||
/**
|
||||
* The reference point MUST be on the baseline of the text.
|
||||
*/
|
||||
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-to-right order. This SHOULD be applied only when the font that is defined in the
|
||||
* playback device context is either Hebrew or Arabic.
|
||||
*/
|
||||
@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.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static final BitField VTA_TOP = BitFieldFactory.getInstance(0x0000);
|
||||
|
||||
/**
|
||||
* The reference point MUST be on the right edge of the bounding rectangle.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static final BitField VTA_RIGHT = BitFieldFactory.getInstance(0x0000);
|
||||
|
||||
/**
|
||||
* The reference point MUST be on the bottom edge of the bounding rectangle.
|
||||
*/
|
||||
private static final BitField VTA_BOTTOM = BitFieldFactory.getInstance(0x0002);
|
||||
|
||||
/**
|
||||
* The reference point MUST be aligned vertically with the center of the bounding
|
||||
* rectangle.
|
||||
*/
|
||||
private static final BitField VTA_CENTER = BitFieldFactory.getInstance(0x0006);
|
||||
|
||||
/**
|
||||
* The reference point MUST be on the left edge of the bounding rectangle.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* TextAlignmentMode Flags:
|
||||
* TA_NOUPDATECP (0x0000):
|
||||
* 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):
|
||||
* The reference point MUST be on the left edge of the bounding rectangle.
|
||||
*
|
||||
* TA_TOP (0x0000):
|
||||
* The reference point MUST be on the top edge of the bounding rectangle.
|
||||
*
|
||||
* TA_UPDATECP (0x0001):
|
||||
* 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):
|
||||
* The reference point MUST be on the right edge of the bounding rectangle.
|
||||
*
|
||||
* TA_CENTER (0x0006):
|
||||
* The reference point MUST be aligned horizontally with the center of the bounding
|
||||
* rectangle.
|
||||
*
|
||||
* TA_BOTTOM (0x0008):
|
||||
* The reference point MUST be on the bottom edge of the bounding rectangle.
|
||||
*
|
||||
* TA_BASELINE (0x0018):
|
||||
* The reference point MUST be on the baseline of the text.
|
||||
*
|
||||
* TA_RTLREADING (0x0100):
|
||||
* 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
|
||||
* playback device context is either Hebrew or Arabic.
|
||||
*
|
||||
*
|
||||
* VerticalTextAlignmentMode Flags (e.g. for Kanji fonts)
|
||||
* VTA_TOP (0x0000):
|
||||
* The reference point MUST be on the top edge of the bounding rectangle.
|
||||
*
|
||||
* VTA_RIGHT (0x0000):
|
||||
* The reference point MUST be on the right edge of the bounding rectangle.
|
||||
*
|
||||
* VTA_BOTTOM (0x0002):
|
||||
* The reference point MUST be on the bottom edge of the bounding rectangle.
|
||||
*
|
||||
* VTA_CENTER (0x0006):
|
||||
* The reference point MUST be aligned vertically with the center of the bounding
|
||||
* rectangle.
|
||||
*
|
||||
* VTA_LEFT (0x0008):
|
||||
* The reference point MUST be on the left edge of the bounding rectangle.
|
||||
*
|
||||
* VTA_BASELINE (0x0018):
|
||||
* The reference point MUST be on the baseline of the text.
|
||||
*/
|
||||
private int textAlignmentMode;
|
||||
|
||||
|
@ -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.");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue