mirror of https://github.com/apache/poi.git
#60656 - Support export file that contains emf and render it correctly
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1845291 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
29587e78df
commit
0a84f6881f
|
@ -26,7 +26,6 @@ public class HemfDrawProperties extends HwmfDrawProperties {
|
|||
|
||||
/** Path for path bracket operations */
|
||||
protected Path2D path = null;
|
||||
protected Shape clip = null;
|
||||
protected boolean usePathBracket = false;
|
||||
|
||||
|
||||
|
@ -67,12 +66,4 @@ public class HemfDrawProperties extends HwmfDrawProperties {
|
|||
public void setUsePathBracket(boolean usePathBracket) {
|
||||
this.usePathBracket = usePathBracket;
|
||||
}
|
||||
|
||||
public Shape getClip() {
|
||||
return clip;
|
||||
}
|
||||
|
||||
public void setClip(Shape shape) {
|
||||
clip = shape;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,16 +22,13 @@ import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_SOLID;
|
|||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.poi.hemf.record.emf.HemfFill;
|
||||
import org.apache.poi.hemf.record.emf.HemfRecord;
|
||||
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
|
||||
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
||||
import org.apache.poi.hwmf.record.HwmfColorRef;
|
||||
import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
|
||||
|
@ -47,36 +44,22 @@ public class HemfGraphics extends HwmfGraphics {
|
|||
private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK);
|
||||
|
||||
|
||||
private final AffineTransform initTrans;
|
||||
|
||||
public HemfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {
|
||||
super(graphicsCtx,bbox);
|
||||
// add dummy entry for object ind ex 0, as emf is 1-based
|
||||
objectIndexes.set(0);
|
||||
initTrans = new AffineTransform(graphicsCtx.getTransform());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HemfDrawProperties getProperties() {
|
||||
if (prop == null) {
|
||||
prop = new HemfDrawProperties();
|
||||
}
|
||||
return (HemfDrawProperties)prop;
|
||||
return (HemfDrawProperties)super.getProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveProperties() {
|
||||
final HemfDrawProperties oldProp = getProperties();
|
||||
oldProp.setClip(graphicsCtx.getClip());
|
||||
propStack.add(oldProp);
|
||||
prop = new HemfDrawProperties(oldProp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreProperties(int index) {
|
||||
super.restoreProperties(index);
|
||||
HemfDrawProperties newProp = getProperties();
|
||||
graphicsCtx.setClip(newProp.getClip());
|
||||
protected HemfDrawProperties newProperties(HwmfDrawProperties oldProps) {
|
||||
return (oldProps == null)
|
||||
? new HemfDrawProperties()
|
||||
: new HemfDrawProperties((HemfDrawProperties)oldProps);
|
||||
}
|
||||
|
||||
public void draw(HemfRecord r) {
|
||||
|
@ -94,8 +77,12 @@ public class HemfGraphics extends HwmfGraphics {
|
|||
} else {
|
||||
path = new Path2D.Double();
|
||||
path.setWindingRule(prop.getWindingRule());
|
||||
}
|
||||
|
||||
// add dummy move-to at start, to handle invalid emfs not containing that move-to
|
||||
if (path.getCurrentPoint() == null) {
|
||||
Point2D pnt = prop.getLocation();
|
||||
path.moveTo(pnt.getX(),pnt.getY());
|
||||
path.moveTo(pnt.getX(), pnt.getY());
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -108,7 +95,12 @@ public class HemfGraphics extends HwmfGraphics {
|
|||
pathConsumer.accept(path);
|
||||
}
|
||||
|
||||
prop.setLocation(path.getCurrentPoint());
|
||||
Point2D curPnt = path.getCurrentPoint();
|
||||
if (curPnt == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
prop.setLocation(curPnt);
|
||||
if (!useBracket) {
|
||||
switch (fillDraw) {
|
||||
case FILL:
|
||||
|
@ -264,64 +256,4 @@ public class HemfGraphics extends HwmfGraphics {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the initial AffineTransform, when this graphics context was created
|
||||
*/
|
||||
public AffineTransform getInitTransform() {
|
||||
return new AffineTransform(initTrans);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current AffineTransform
|
||||
*/
|
||||
public AffineTransform getTransform() {
|
||||
return new AffineTransform(graphicsCtx.getTransform());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current AffineTransform
|
||||
* @param tx the current AffineTransform
|
||||
*/
|
||||
public void setTransform(AffineTransform tx) {
|
||||
graphicsCtx.setTransform(tx);
|
||||
}
|
||||
|
||||
public void setClip(Shape clip, HemfFill.HemfRegionMode regionMode) {
|
||||
Shape oldClip = graphicsCtx.getClip();
|
||||
|
||||
switch (regionMode) {
|
||||
case RGN_AND:
|
||||
graphicsCtx.clip(clip);
|
||||
break;
|
||||
case RGN_OR:
|
||||
if (oldClip == null) {
|
||||
graphicsCtx.setClip(clip);
|
||||
} else {
|
||||
Area area = new Area(oldClip);
|
||||
area.add(new Area(clip));
|
||||
graphicsCtx.setClip(area);
|
||||
}
|
||||
break;
|
||||
case RGN_XOR:
|
||||
if (oldClip == null) {
|
||||
graphicsCtx.setClip(clip);
|
||||
} else {
|
||||
Area area = new Area(oldClip);
|
||||
area.exclusiveOr(new Area(clip));
|
||||
graphicsCtx.setClip(area);
|
||||
}
|
||||
break;
|
||||
case RGN_DIFF:
|
||||
if (oldClip != null) {
|
||||
Area area = new Area(oldClip);
|
||||
area.subtract(new Area(clip));
|
||||
graphicsCtx.setClip(area);
|
||||
}
|
||||
break;
|
||||
case RGN_COPY:
|
||||
graphicsCtx.setClip(clip);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1051,7 +1051,11 @@ public class HemfDraw {
|
|||
@Override
|
||||
public void draw(HemfGraphics ctx) {
|
||||
final HemfDrawProperties prop = ctx.getProperties();
|
||||
final Path2D path = (Path2D)prop.getPath().clone();
|
||||
final Path2D origPath = prop.getPath();
|
||||
if (origPath.getCurrentPoint() == null) {
|
||||
return;
|
||||
}
|
||||
final Path2D path = (Path2D)origPath.clone();
|
||||
path.closePath();
|
||||
path.setWindingRule(ctx.getProperties().getWindingRule());
|
||||
ctx.fill(path);
|
||||
|
|
|
@ -20,10 +20,11 @@ package org.apache.poi.hemf.record.emf;
|
|||
import static org.apache.poi.hemf.record.emf.HemfDraw.readPointL;
|
||||
import static org.apache.poi.hemf.record.emf.HemfDraw.readRectL;
|
||||
import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE;
|
||||
import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
|
||||
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -36,11 +37,11 @@ import org.apache.poi.hemf.draw.HemfDrawProperties;
|
|||
import org.apache.poi.hemf.draw.HemfGraphics;
|
||||
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
||||
import org.apache.poi.hwmf.record.HwmfBitmapDib;
|
||||
import org.apache.poi.hwmf.record.HwmfBrushStyle;
|
||||
import org.apache.poi.hwmf.record.HwmfColorRef;
|
||||
import org.apache.poi.hwmf.record.HwmfDraw;
|
||||
import org.apache.poi.hwmf.record.HwmfFill;
|
||||
import org.apache.poi.hwmf.record.HwmfFill.ColorUsage;
|
||||
import org.apache.poi.hwmf.record.HwmfRegionMode;
|
||||
import org.apache.poi.hwmf.record.HwmfTernaryRasterOp;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
@ -50,47 +51,6 @@ import org.apache.poi.util.LittleEndianInputStream;
|
|||
public class HemfFill {
|
||||
private static final int MAX_RECORD_LENGTH = 10_000_000;
|
||||
|
||||
public enum HemfRegionMode {
|
||||
/**
|
||||
* The new clipping region includes the intersection (overlapping areas)
|
||||
* of the current clipping region and the current path (or new region).
|
||||
*/
|
||||
RGN_AND(0x01),
|
||||
/**
|
||||
* The new clipping region includes the union (combined areas)
|
||||
* of the current clipping region and the current path (or new region).
|
||||
*/
|
||||
RGN_OR(0x02),
|
||||
/**
|
||||
* The new clipping region includes the union of the current clipping region
|
||||
* and the current path (or new region) but without the overlapping areas
|
||||
*/
|
||||
RGN_XOR(0x03),
|
||||
/**
|
||||
* The new clipping region includes the areas of the current clipping region
|
||||
* with those of the current path (or new region) excluded.
|
||||
*/
|
||||
RGN_DIFF(0x04),
|
||||
/**
|
||||
* The new clipping region is the current path (or the new region).
|
||||
*/
|
||||
RGN_COPY(0x05);
|
||||
|
||||
int flag;
|
||||
HemfRegionMode(int flag) {
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
public static HemfRegionMode valueOf(int flag) {
|
||||
for (HemfRegionMode rm : values()) {
|
||||
if (rm.flag == flag) return rm;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The EMR_SETPOLYFILLMODE record defines polygon fill mode.
|
||||
*/
|
||||
|
@ -105,7 +65,7 @@ public class HemfFill {
|
|||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
||||
// A 32-bit unsigned integer that specifies the polygon fill mode and
|
||||
// MUST be in the PolygonFillMode enumeration.
|
||||
polyfillMode = HwmfPolyfillMode.valueOf((int)leis.readUInt());
|
||||
polyFillMode = HwmfPolyfillMode.valueOf((int)leis.readUInt());
|
||||
return LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +93,7 @@ public class HemfFill {
|
|||
* optionally in combination with a brush pattern, according to a specified raster operation, stretching or
|
||||
* compressing the output to fit the dimensions of the destination, if necessary.
|
||||
*/
|
||||
public static class EmfStretchBlt extends HwmfFill.WmfBitBlt implements HemfRecord {
|
||||
public static class EmfStretchBlt extends HwmfFill.WmfStretchDib implements HemfRecord {
|
||||
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
||||
|
||||
/** An XForm object that specifies a world-space to page-space transform to apply to the source bitmap. */
|
||||
|
@ -142,14 +102,6 @@ public class HemfFill {
|
|||
/** A WMF ColorRef object that specifies the background color of the source bitmap. */
|
||||
protected final HwmfColorRef bkColorSrc = new HwmfColorRef();
|
||||
|
||||
/**
|
||||
* A 32-bit unsigned integer that specifies how to interpret values in the color table in
|
||||
* the source bitmap header. This value MUST be in the DIBColors enumeration
|
||||
*/
|
||||
protected int usageSrc;
|
||||
|
||||
protected final HwmfBitmapDib bitmap = new HwmfBitmapDib();
|
||||
|
||||
@Override
|
||||
public HemfRecordType getEmfRecordType() {
|
||||
return HemfRecordType.stretchBlt;
|
||||
|
@ -168,7 +120,7 @@ public class HemfFill {
|
|||
// rectangle and optionally a brush pattern, to achieve the final color.
|
||||
int rasterOpIndex = (int)leis.readUInt();
|
||||
|
||||
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
|
||||
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex >>> 16);
|
||||
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
|
||||
|
@ -179,7 +131,7 @@ public class HemfFill {
|
|||
|
||||
size += bkColorSrc.init(leis);
|
||||
|
||||
usageSrc = (int)leis.readUInt();
|
||||
colorUsage = ColorUsage.valueOf((int)leis.readUInt());
|
||||
|
||||
// A 32-bit unsigned integer that specifies the offset, in bytes, from the
|
||||
// start of this record to the source bitmap header in the BitmapBuffer field.
|
||||
|
@ -188,7 +140,7 @@ public class HemfFill {
|
|||
// A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header.
|
||||
final int cbBmiSrc = (int)leis.readUInt();
|
||||
size += 3*LittleEndianConsts.INT_SIZE;
|
||||
if (size <= recordSize) {
|
||||
if (size >= recordSize) {
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@ -198,9 +150,12 @@ public class HemfFill {
|
|||
|
||||
// A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits.
|
||||
final int cbBitsSrc = (int)leis.readUInt();
|
||||
|
||||
size += 2*LittleEndianConsts.INT_SIZE;
|
||||
|
||||
if (size >= recordSize) {
|
||||
return size;
|
||||
}
|
||||
|
||||
if (srcEqualsDstDimension()) {
|
||||
srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight());
|
||||
} else {
|
||||
|
@ -219,14 +174,20 @@ public class HemfFill {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(HemfGraphics ctx) {
|
||||
HemfDrawProperties prop = ctx.getProperties();
|
||||
prop.setBackgroundColor(this.bkColorSrc);
|
||||
super.draw(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return
|
||||
"{ bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+"}"+
|
||||
"{ bounds: "+boundsToString(bounds)+
|
||||
", xFormSrc: { scaleX: "+xFormSrc.getScaleX()+", shearX: "+xFormSrc.getShearX()+", transX: "+xFormSrc.getTranslateX()+", scaleY: "+xFormSrc.getScaleY()+", shearY: "+xFormSrc.getShearY()+", transY: "+xFormSrc.getTranslateY()+" }"+
|
||||
", bkColorSrc: "+bkColorSrc+
|
||||
", usageSrc: "+usageSrc+", "
|
||||
+ super.toString().substring(1);
|
||||
","+super.toString().substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,7 +240,8 @@ public class HemfFill {
|
|||
// These codes define how the color data of the source rectangle is to be combined with the color data
|
||||
// of the destination rectangle and optionally a brush pattern, to achieve the final color.
|
||||
// The value MUST be in the WMF Ternary Raster Operation enumeration
|
||||
rasterOperation = HwmfTernaryRasterOp.valueOf(leis.readInt());
|
||||
int rasterOpIndex = (int)leis.readUInt();
|
||||
rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex >>> 16);
|
||||
|
||||
// A 32-bit signed integer that specifies the logical width of the destination rectangle.
|
||||
int cxDest = leis.readInt();
|
||||
|
@ -291,7 +253,7 @@ public class HemfFill {
|
|||
|
||||
size += 8*LittleEndianConsts.INT_SIZE;
|
||||
|
||||
size += readBitmap(leis, dib, startIdx, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc);
|
||||
size += readBitmap(leis, bitmap, startIdx, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
@ -346,7 +308,7 @@ public class HemfFill {
|
|||
ctx.fill(getShape());
|
||||
}
|
||||
|
||||
protected Area getShape() {
|
||||
protected Shape getShape() {
|
||||
return getRgnShape(rgnRects);
|
||||
}
|
||||
}
|
||||
|
@ -371,7 +333,7 @@ public class HemfFill {
|
|||
return size;
|
||||
}
|
||||
|
||||
protected Area getShape() {
|
||||
protected Shape getShape() {
|
||||
return getRgnShape(rgnRects);
|
||||
}
|
||||
}
|
||||
|
@ -408,13 +370,13 @@ public class HemfFill {
|
|||
return size;
|
||||
}
|
||||
|
||||
protected Area getShape() {
|
||||
protected Shape getShape() {
|
||||
return getRgnShape(rgnRects);
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfExtSelectClipRgn implements HemfRecord {
|
||||
protected HemfRegionMode regionMode;
|
||||
protected HwmfRegionMode regionMode;
|
||||
protected final List<Rectangle2D> rgnRects = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
|
@ -427,20 +389,43 @@ public class HemfFill {
|
|||
// A 32-bit unsigned integer that specifies the size of region data in bytes
|
||||
long rgnDataSize = leis.readUInt();
|
||||
// A 32-bit unsigned integer that specifies the way to use the region.
|
||||
regionMode = HemfRegionMode.valueOf((int)leis.readUInt());
|
||||
regionMode = HwmfRegionMode.valueOf((int)leis.readUInt());
|
||||
long size = 2* LittleEndianConsts.INT_SIZE;
|
||||
|
||||
// If RegionMode is RGN_COPY, this data can be omitted and the clip region
|
||||
// SHOULD be set to the default (NULL) clip region.
|
||||
if (regionMode != HemfRegionMode.RGN_COPY) {
|
||||
if (regionMode != HwmfRegionMode.RGN_COPY) {
|
||||
size += readRgnData(leis, rgnRects);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
protected Area getShape() {
|
||||
protected Shape getShape() {
|
||||
return getRgnShape(rgnRects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(HemfGraphics ctx) {
|
||||
HemfDrawProperties prop = ctx.getProperties();
|
||||
ctx.setClip(getShape(), regionMode, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{ regionMode: '"+regionMode+"'");
|
||||
sb.append(", regions: [");
|
||||
boolean isFirst = true;
|
||||
for (Rectangle2D r : rgnRects) {
|
||||
if (!isFirst) {
|
||||
sb.append(",");
|
||||
}
|
||||
isFirst = false;
|
||||
sb.append(boundsToString(r));
|
||||
}
|
||||
sb.append("]}");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmfAlphaBlend implements HemfRecord {
|
||||
|
@ -717,7 +702,10 @@ public class HemfFill {
|
|||
return 6 * LittleEndian.INT_SIZE;
|
||||
}
|
||||
|
||||
protected static Area getRgnShape(List<Rectangle2D> rgnRects) {
|
||||
protected static Shape getRgnShape(List<Rectangle2D> rgnRects) {
|
||||
if (rgnRects.size() == 1) {
|
||||
return rgnRects.get(0);
|
||||
}
|
||||
final Area frame = new Area();
|
||||
rgnRects.forEach((rct) -> frame.add(new Area(rct)));
|
||||
return frame;
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.apache.poi.hemf.record.emf;
|
|||
import static org.apache.poi.hemf.record.emf.HemfDraw.readDimensionInt;
|
||||
import static org.apache.poi.hemf.record.emf.HemfDraw.readRectL;
|
||||
import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE;
|
||||
import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
|
||||
import static org.apache.poi.hwmf.record.HwmfDraw.dimToString;
|
||||
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
@ -119,21 +121,21 @@ public class HemfHeader implements HemfRecord {
|
|||
@Override
|
||||
public String toString() {
|
||||
return "HemfHeader{" +
|
||||
"boundsRectangle=" + boundsRectangle +
|
||||
", frameRectangle=" + frameRectangle +
|
||||
", bytes=" + bytes +
|
||||
", records=" + records +
|
||||
", handles=" + handles +
|
||||
", description=" + description +
|
||||
", nPalEntries=" + nPalEntries +
|
||||
", hasExtension1=" + hasExtension1 +
|
||||
", cbPixelFormat=" + cbPixelFormat +
|
||||
", offPixelFormat=" + offPixelFormat +
|
||||
", bOpenGL=" + bOpenGL +
|
||||
", hasExtension2=" + hasExtension2 +
|
||||
", deviceDimension=" + deviceDimension +
|
||||
", microDimension=" + microDimension +
|
||||
", milliDimension=" + milliDimension +
|
||||
"boundsRectangle: " + boundsToString(boundsRectangle) +
|
||||
", frameRectangle: " + boundsToString(frameRectangle) +
|
||||
", bytes: " + bytes +
|
||||
", records: " + records +
|
||||
", handles: " + handles +
|
||||
", description: '" + description + "'" +
|
||||
", nPalEntries: " + nPalEntries +
|
||||
", hasExtension1: " + hasExtension1 +
|
||||
", cbPixelFormat: " + cbPixelFormat +
|
||||
", offPixelFormat: " + offPixelFormat +
|
||||
", bOpenGL: " + bOpenGL +
|
||||
", hasExtension2: " + hasExtension2 +
|
||||
", deviceDimension: " + dimToString(deviceDimension) +
|
||||
", microDimension: " + dimToString(microDimension) +
|
||||
", milliDimension: " + dimToString(milliDimension) +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ public class HemfText {
|
|||
|
||||
@Override
|
||||
public void draw(HwmfGraphics ctx) {
|
||||
ctx.drawString(rawTextBytes, reference, bounds, dx, isUnicode());
|
||||
ctx.drawString(rawTextBytes, reference, bounds, options, dx, isUnicode());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,7 +24,7 @@ import java.io.IOException;
|
|||
|
||||
import org.apache.poi.hemf.draw.HemfDrawProperties;
|
||||
import org.apache.poi.hemf.draw.HemfGraphics;
|
||||
import org.apache.poi.hemf.record.emf.HemfFill.HemfRegionMode;
|
||||
import org.apache.poi.hwmf.record.HwmfRegionMode;
|
||||
import org.apache.poi.hwmf.record.HwmfWindowing;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
|
@ -188,7 +188,7 @@ public class HemfWindowing {
|
|||
* device context, combining the new region with any existing clipping region using the specified mode.
|
||||
*/
|
||||
public static class EmfSelectClipPath implements HemfRecord {
|
||||
protected HemfRegionMode regionMode;
|
||||
protected HwmfRegionMode regionMode;
|
||||
|
||||
@Override
|
||||
public HemfRecordType getEmfRecordType() {
|
||||
|
@ -199,15 +199,15 @@ public class HemfWindowing {
|
|||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
||||
// A 32-bit unsigned integer that specifies the way to use the path.
|
||||
// The value MUST be in the RegionMode enumeration
|
||||
regionMode = HemfRegionMode.valueOf(leis.readInt());
|
||||
regionMode = HwmfRegionMode.valueOf(leis.readInt());
|
||||
|
||||
return LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(HemfGraphics ctx) {
|
||||
HemfDrawProperties props = ctx.getProperties();
|
||||
ctx.setClip(props.getPath(), regionMode);
|
||||
HemfDrawProperties prop = ctx.getProperties();
|
||||
ctx.setClip(prop.getPath(), regionMode, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -113,7 +113,7 @@ public class HemfPicture implements Iterable<HemfRecord> {
|
|||
height = 100;
|
||||
}
|
||||
|
||||
return new Dimension2DDouble(width*coeff, height*coeff);
|
||||
return new Dimension2DDouble(Math.abs(width*coeff), Math.abs(height*coeff));
|
||||
}
|
||||
|
||||
public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) {
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.poi.hwmf.draw;
|
|||
|
||||
import java.awt.Color;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Point2D;
|
||||
|
@ -67,6 +68,8 @@ public class HwmfDrawProperties {
|
|||
private HwmfTextAlignment textAlignAsian;
|
||||
private HwmfTextVerticalAlignment textVAlignAsian;
|
||||
private HwmfTernaryRasterOp rasterOp;
|
||||
protected Shape clip;
|
||||
protected final AffineTransform transform = new AffineTransform();
|
||||
|
||||
public HwmfDrawProperties() {
|
||||
window = new Rectangle2D.Double(0, 0, 1, 1);
|
||||
|
@ -89,6 +92,7 @@ public class HwmfDrawProperties {
|
|||
textAlignAsian = HwmfTextAlignment.RIGHT;
|
||||
textVAlignAsian = HwmfTextVerticalAlignment.TOP;
|
||||
rasterOp = HwmfTernaryRasterOp.PATCOPY;
|
||||
clip = null;
|
||||
}
|
||||
|
||||
public HwmfDrawProperties(HwmfDrawProperties other) {
|
||||
|
@ -126,6 +130,8 @@ public class HwmfDrawProperties {
|
|||
this.textAlignAsian = other.textAlignAsian;
|
||||
this.textVAlignAsian = other.textVAlignAsian;
|
||||
this.rasterOp = other.rasterOp;
|
||||
this.transform.setTransform(other.transform);
|
||||
this.clip = other.clip;
|
||||
}
|
||||
|
||||
public void setViewportExt(double width, double height) {
|
||||
|
@ -387,4 +393,20 @@ public class HwmfDrawProperties {
|
|||
public void setRasterOp(HwmfTernaryRasterOp rasterOp) {
|
||||
this.rasterOp = rasterOp;
|
||||
}
|
||||
|
||||
public AffineTransform getTransform() {
|
||||
return transform;
|
||||
}
|
||||
|
||||
public void setTransform(AffineTransform transform) {
|
||||
this.transform.setTransform(transform);
|
||||
}
|
||||
|
||||
public Shape getClip() {
|
||||
return clip;
|
||||
}
|
||||
|
||||
public void setClip(Shape clip) {
|
||||
this.clip = clip;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.awt.font.FontRenderContext;
|
|||
import java.awt.font.TextAttribute;
|
||||
import java.awt.font.TextLayout;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
@ -50,7 +51,9 @@ 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.hwmf.record.HwmfRegionMode;
|
||||
import org.apache.poi.hwmf.record.HwmfText;
|
||||
import org.apache.poi.hwmf.record.HwmfText.WmfExtTextOutOptions;
|
||||
import org.apache.poi.sl.draw.DrawFactory;
|
||||
import org.apache.poi.sl.draw.DrawFontManager;
|
||||
import org.apache.poi.util.LocaleUtil;
|
||||
|
@ -66,11 +69,12 @@ public class HwmfGraphics {
|
|||
protected final Graphics2D graphicsCtx;
|
||||
protected final BitSet objectIndexes = new BitSet();
|
||||
protected final TreeMap<Integer,HwmfObjectTableEntry> objectTable = new TreeMap<>();
|
||||
protected final AffineTransform initialAT = new AffineTransform();
|
||||
|
||||
|
||||
private static final Charset DEFAULT_CHARSET = LocaleUtil.CHARSET_1252;
|
||||
/** Bounding box from the placeable header */
|
||||
private final Rectangle2D bbox;
|
||||
private final AffineTransform initialAT;
|
||||
|
||||
/**
|
||||
* Initialize a graphics context for wmf rendering
|
||||
|
@ -81,16 +85,20 @@ public class HwmfGraphics {
|
|||
public HwmfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {
|
||||
this.graphicsCtx = graphicsCtx;
|
||||
this.bbox = (Rectangle2D)bbox.clone();
|
||||
this.initialAT = graphicsCtx.getTransform();
|
||||
this.initialAT.setTransform(graphicsCtx.getTransform());
|
||||
}
|
||||
|
||||
public HwmfDrawProperties getProperties() {
|
||||
if (prop == null) {
|
||||
prop = new HwmfDrawProperties();
|
||||
prop = newProperties(null);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
protected HwmfDrawProperties newProperties(HwmfDrawProperties oldProps) {
|
||||
return (oldProps == null) ? new HwmfDrawProperties() : new HwmfDrawProperties(oldProps);
|
||||
}
|
||||
|
||||
public void draw(Shape shape) {
|
||||
HwmfPenStyle ps = getProperties().getPenStyle();
|
||||
if (ps == null) {
|
||||
|
@ -119,14 +127,18 @@ public class HwmfGraphics {
|
|||
}
|
||||
|
||||
public void fill(Shape shape) {
|
||||
if (getProperties().getBrushStyle() != HwmfBrushStyle.BS_NULL) {
|
||||
// GeneralPath gp = new GeneralPath(shape);
|
||||
// gp.setWindingRule(getProperties().getPolyfillMode().awtFlag);
|
||||
HwmfDrawProperties prop = getProperties();
|
||||
if (prop.getBrushStyle() != HwmfBrushStyle.BS_NULL) {
|
||||
if (prop.getBkMode() == HwmfBkMode.OPAQUE) {
|
||||
graphicsCtx.setPaint(prop.getBackgroundColor().getColor());
|
||||
graphicsCtx.fill(shape);
|
||||
}
|
||||
|
||||
graphicsCtx.setPaint(getFill());
|
||||
graphicsCtx.fill(shape);
|
||||
}
|
||||
|
||||
draw(shape);
|
||||
// draw(shape);
|
||||
}
|
||||
|
||||
protected BasicStroke getStroke() {
|
||||
|
@ -264,8 +276,10 @@ public class HwmfGraphics {
|
|||
public void saveProperties() {
|
||||
final HwmfDrawProperties p = getProperties();
|
||||
assert(p != null);
|
||||
p.setTransform(graphicsCtx.getTransform());
|
||||
p.setClip(graphicsCtx.getClip());
|
||||
propStack.add(p);
|
||||
prop = new HwmfDrawProperties(p);
|
||||
prop = newProperties(p);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -291,7 +305,16 @@ public class HwmfGraphics {
|
|||
// roll to last when curIdx == 0
|
||||
stackIndex = propStack.size()-1;
|
||||
}
|
||||
prop = propStack.get(stackIndex);
|
||||
|
||||
// The playback device context is restored by popping state information off a stack that was created by
|
||||
// prior SAVEDC records
|
||||
// ... so because being a stack, we will remove all entries having a greater index than the stackIndex
|
||||
for (int i=propStack.size()-1; i>=stackIndex; i--) {
|
||||
prop = propStack.remove(i);
|
||||
}
|
||||
|
||||
graphicsCtx.setTransform(prop.getTransform());
|
||||
graphicsCtx.setClip(prop.getClip());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -338,13 +361,14 @@ public class HwmfGraphics {
|
|||
}
|
||||
}
|
||||
|
||||
public void drawString(byte[] text, Point2D reference, Rectangle2D clip) {
|
||||
drawString(text, reference, clip, null, false);
|
||||
public void drawString(byte[] text, Point2D reference) {
|
||||
drawString(text, reference, null, null, null, false);
|
||||
}
|
||||
|
||||
public void drawString(byte[] text, Point2D reference, Rectangle2D clip, List<Integer> dx, boolean isUnicode) {
|
||||
public void drawString(byte[] text, Point2D reference, Rectangle2D clip, WmfExtTextOutOptions opts, List<Integer> dx, boolean isUnicode) {
|
||||
final HwmfDrawProperties prop = getProperties();
|
||||
|
||||
HwmfFont font = getProperties().getFont();
|
||||
HwmfFont font = prop.getFont();
|
||||
if (font == null || text == null || text.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -446,21 +470,31 @@ public class HwmfGraphics {
|
|||
Point2D dst = new Point2D.Double();
|
||||
tx.transform(src, dst);
|
||||
|
||||
// TODO: implement clipping on bounds
|
||||
final Shape clipShape = graphicsCtx.getClip();
|
||||
final AffineTransform at = graphicsCtx.getTransform();
|
||||
try {
|
||||
if (clip != null) {
|
||||
graphicsCtx.translate(-clip.getCenterX(), -clip.getCenterY());
|
||||
graphicsCtx.rotate(angle);
|
||||
graphicsCtx.translate(clip.getCenterX(), clip.getCenterY());
|
||||
if (prop.getBkMode() == HwmfBkMode.OPAQUE && opts.isOpaque()) {
|
||||
graphicsCtx.setPaint(prop.getBackgroundColor().getColor());
|
||||
graphicsCtx.fill(clip);
|
||||
}
|
||||
if (opts.isClipped()) {
|
||||
graphicsCtx.setClip(clip);
|
||||
}
|
||||
graphicsCtx.setTransform(at);
|
||||
}
|
||||
|
||||
graphicsCtx.translate(reference.getX(), reference.getY());
|
||||
graphicsCtx.rotate(angle);
|
||||
graphicsCtx.translate(dst.getX(), dst.getY());
|
||||
if (getProperties().getBkMode() == HwmfBkMode.OPAQUE && clip != null) {
|
||||
// TODO: validate bounds
|
||||
graphicsCtx.setBackground(getProperties().getBackgroundColor().getColor());
|
||||
graphicsCtx.fill(new Rectangle2D.Double(0, 0, clip.getWidth(), clip.getHeight()));
|
||||
}
|
||||
graphicsCtx.setColor(getProperties().getTextColor().getColor());
|
||||
graphicsCtx.setColor(prop.getTextColor().getColor());
|
||||
graphicsCtx.drawString(as.getIterator(), 0, 0);
|
||||
} finally {
|
||||
graphicsCtx.setTransform(at);
|
||||
graphicsCtx.setClip(clipShape);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -498,18 +532,136 @@ public class HwmfGraphics {
|
|||
}
|
||||
|
||||
public void drawImage(BufferedImage img, Rectangle2D srcBounds, Rectangle2D dstBounds) {
|
||||
// prop.getRasterOp();
|
||||
graphicsCtx.drawImage(img,
|
||||
(int)dstBounds.getX(),
|
||||
(int)dstBounds.getY(),
|
||||
(int)(dstBounds.getX()+dstBounds.getWidth()),
|
||||
(int)(dstBounds.getY()+dstBounds.getHeight()),
|
||||
(int)srcBounds.getX(),
|
||||
(int)srcBounds.getY(),
|
||||
(int)(srcBounds.getX()+srcBounds.getWidth()),
|
||||
(int)(srcBounds.getY()+srcBounds.getHeight()),
|
||||
null, // getProperties().getBackgroundColor().getColor(),
|
||||
null
|
||||
);
|
||||
HwmfDrawProperties prop = getProperties();
|
||||
|
||||
// handle raster op
|
||||
// currently the raster op as described in https://docs.microsoft.com/en-us/windows/desktop/gdi/ternary-raster-operations
|
||||
// are not supported, as we would need to extract the destination image area from the underlying buffered image
|
||||
// and therefore would make it mandatory that the graphics context must be from a buffered image
|
||||
// furthermore I doubt the purpose of bitwise image operations on non-black/white images
|
||||
switch (prop.getRasterOp()) {
|
||||
case D:
|
||||
// keep destination, i.e. do nothing
|
||||
break;
|
||||
case PATCOPY:
|
||||
graphicsCtx.setPaint(getFill());
|
||||
graphicsCtx.fill(dstBounds);
|
||||
break;
|
||||
case BLACKNESS:
|
||||
graphicsCtx.setPaint(Color.BLACK);
|
||||
graphicsCtx.fill(dstBounds);
|
||||
break;
|
||||
case WHITENESS:
|
||||
graphicsCtx.setPaint(Color.WHITE);
|
||||
graphicsCtx.fill(dstBounds);
|
||||
break;
|
||||
default:
|
||||
case SRCCOPY:
|
||||
final Shape clip = graphicsCtx.getClip();
|
||||
|
||||
// add clipping in case of a source subimage, i.e. a clipped source image
|
||||
// some dstBounds are horizontal or vertical flipped, so we need to normalize the images
|
||||
Rectangle2D normalized = new Rectangle2D.Double(
|
||||
dstBounds.getWidth() >= 0 ? dstBounds.getMinX() : dstBounds.getMaxX(),
|
||||
dstBounds.getHeight() >= 0 ? dstBounds.getMinY() : dstBounds.getMaxY(),
|
||||
Math.abs(dstBounds.getWidth()),
|
||||
Math.abs(dstBounds.getHeight()));
|
||||
graphicsCtx.clip(normalized);
|
||||
final AffineTransform at = graphicsCtx.getTransform();
|
||||
|
||||
final Rectangle2D imgBounds = new Rectangle2D.Double(0,0,img.getWidth(),img.getHeight());
|
||||
final boolean isImgBounds = (srcBounds.equals(new Rectangle2D.Double()));
|
||||
final Rectangle2D srcBounds2 = isImgBounds ? imgBounds : srcBounds;
|
||||
|
||||
// TODO: apply emf transform
|
||||
graphicsCtx.translate(dstBounds.getX(), dstBounds.getY());
|
||||
graphicsCtx.scale(dstBounds.getWidth()/srcBounds2.getWidth(), dstBounds.getHeight()/srcBounds2.getHeight());
|
||||
graphicsCtx.translate(-srcBounds2.getX(), -srcBounds2.getY());
|
||||
|
||||
graphicsCtx.drawImage(img, 0, 0, prop.getBackgroundColor().getColor(), null);
|
||||
|
||||
graphicsCtx.setTransform(at);
|
||||
graphicsCtx.setClip(clip);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the initial AffineTransform, when this graphics context was created
|
||||
*/
|
||||
public AffineTransform getInitTransform() {
|
||||
return new AffineTransform(initialAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current AffineTransform
|
||||
*/
|
||||
public AffineTransform getTransform() {
|
||||
return new AffineTransform(graphicsCtx.getTransform());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current AffineTransform
|
||||
* @param tx the current AffineTransform
|
||||
*/
|
||||
public void setTransform(AffineTransform tx) {
|
||||
graphicsCtx.setTransform(tx);
|
||||
}
|
||||
|
||||
private static int clipCnt = 0;
|
||||
|
||||
public void setClip(Shape clip, HwmfRegionMode regionMode, boolean useInitialAT) {
|
||||
final AffineTransform at = graphicsCtx.getTransform();
|
||||
if (useInitialAT) {
|
||||
graphicsCtx.setTransform(initialAT);
|
||||
}
|
||||
final Shape oldClip = graphicsCtx.getClip();
|
||||
final boolean isEmpty = clip.getBounds2D().isEmpty();
|
||||
switch (regionMode) {
|
||||
case RGN_AND:
|
||||
if (!isEmpty) {
|
||||
graphicsCtx.clip(clip);
|
||||
}
|
||||
break;
|
||||
case RGN_OR:
|
||||
if (!isEmpty) {
|
||||
if (oldClip == null) {
|
||||
graphicsCtx.setClip(clip);
|
||||
} else {
|
||||
Area area = new Area(oldClip);
|
||||
area.add(new Area(clip));
|
||||
graphicsCtx.setClip(area);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RGN_XOR:
|
||||
if (!isEmpty) {
|
||||
if (oldClip == null) {
|
||||
graphicsCtx.setClip(clip);
|
||||
} else {
|
||||
Area area = new Area(oldClip);
|
||||
area.exclusiveOr(new Area(clip));
|
||||
graphicsCtx.setClip(area);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RGN_DIFF:
|
||||
if (!isEmpty) {
|
||||
if (oldClip != null) {
|
||||
Area area = new Area(oldClip);
|
||||
area.subtract(new Area(clip));
|
||||
graphicsCtx.setClip(area);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RGN_COPY: {
|
||||
graphicsCtx.setClip(isEmpty ? null : clip);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (useInitialAT) {
|
||||
graphicsCtx.setTransform(at);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -413,7 +413,7 @@ public class HwmfBitmapDib {
|
|||
return new ByteArrayInputStream(getBMPData());
|
||||
}
|
||||
|
||||
private byte[] getBMPData() {
|
||||
public byte[] getBMPData() {
|
||||
if (imageData == null) {
|
||||
throw new RecordFormatException("bitmap not initialized ... need to call init() before");
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.poi.hwmf.record;
|
|||
import java.awt.Shape;
|
||||
import java.awt.geom.Arc2D;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.geom.Path2D;
|
||||
|
@ -744,4 +745,11 @@ public class HwmfDraw {
|
|||
public static String boundsToString(Rectangle2D bounds) {
|
||||
return "{ x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+" }";
|
||||
}
|
||||
|
||||
@Internal
|
||||
public static String dimToString(Dimension2D dim) {
|
||||
return "{ w: "+dim.getWidth()+", h: "+dim.getHeight()+" }";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.awt.geom.Rectangle2D;
|
|||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
|
||||
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
|
@ -222,7 +223,7 @@ public class HwmfFill {
|
|||
* An unsigned integer that defines polygon fill mode.
|
||||
* This MUST be one of the values: ALTERNATE = 0x0001, WINDING = 0x0002
|
||||
*/
|
||||
protected HwmfPolyfillMode polyfillMode;
|
||||
protected HwmfPolyfillMode polyFillMode;
|
||||
|
||||
@Override
|
||||
public HwmfRecordType getWmfRecordType() {
|
||||
|
@ -231,13 +232,18 @@ public class HwmfFill {
|
|||
|
||||
@Override
|
||||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
||||
polyfillMode = HwmfPolyfillMode.valueOf(leis.readUShort() & 3);
|
||||
polyFillMode = HwmfPolyfillMode.valueOf(leis.readUShort() & 3);
|
||||
return LittleEndianConsts.SHORT_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(HwmfGraphics ctx) {
|
||||
ctx.getProperties().setPolyfillMode(polyfillMode);
|
||||
ctx.getProperties().setPolyfillMode(polyFillMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{ polyFillMode: '"+ polyFillMode +"' }";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -458,7 +464,7 @@ public class HwmfFill {
|
|||
* A variable-sized DeviceIndependentBitmap Object (section 2.2.2.9) that is the
|
||||
* source of the color data.
|
||||
*/
|
||||
protected final HwmfBitmapDib dib = new HwmfBitmapDib();
|
||||
protected final HwmfBitmapDib bitmap = new HwmfBitmapDib();
|
||||
|
||||
@Override
|
||||
public HwmfRecordType getWmfRecordType() {
|
||||
|
@ -481,22 +487,36 @@ public class HwmfFill {
|
|||
size += readBounds2(leis, srcBounds);
|
||||
size += readBounds2(leis, dstBounds);
|
||||
|
||||
size += dib.init(leis, (int)(recordSize-6-size));
|
||||
size += bitmap.init(leis, (int)(recordSize-6-size));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(HwmfGraphics ctx) {
|
||||
if (dib.isValid()) {
|
||||
ctx.getProperties().setRasterOp(rasterOperation);
|
||||
HwmfDrawProperties prop = ctx.getProperties();
|
||||
prop.setRasterOp(rasterOperation);
|
||||
if (bitmap.isValid()) {
|
||||
ctx.drawImage(getImage(), srcBounds, dstBounds);
|
||||
} else {
|
||||
BufferedImage bi = new BufferedImage((int)dstBounds.getWidth(), (int)dstBounds.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
ctx.drawImage(bi, dstBounds, dstBounds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage getImage() {
|
||||
return dib.getImage();
|
||||
return bitmap.getImage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return
|
||||
"{ rasterOperation: '"+rasterOperation+"'"+
|
||||
", colorUsage: '"+colorUsage+"'"+
|
||||
", srcBounds: "+boundsToString(srcBounds)+
|
||||
", dstBounds: "+boundsToString(dstBounds)+
|
||||
"}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hwmf.record;
|
||||
|
||||
import org.apache.poi.hemf.record.emf.HemfFill;
|
||||
|
||||
public enum HwmfRegionMode {
|
||||
/**
|
||||
* The new clipping region includes the intersection (overlapping areas)
|
||||
* of the current clipping region and the current path (or new region).
|
||||
*/
|
||||
RGN_AND(0x01),
|
||||
/**
|
||||
* The new clipping region includes the union (combined areas)
|
||||
* of the current clipping region and the current path (or new region).
|
||||
*/
|
||||
RGN_OR(0x02),
|
||||
/**
|
||||
* The new clipping region includes the union of the current clipping region
|
||||
* and the current path (or new region) but without the overlapping areas
|
||||
*/
|
||||
RGN_XOR(0x03),
|
||||
/**
|
||||
* The new clipping region includes the areas of the current clipping region
|
||||
* with those of the current path (or new region) excluded.
|
||||
*/
|
||||
RGN_DIFF(0x04),
|
||||
/**
|
||||
* The new clipping region is the current path (or the new region).
|
||||
*/
|
||||
RGN_COPY(0x05);
|
||||
|
||||
int flag;
|
||||
HwmfRegionMode(int flag) {
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
public static HwmfRegionMode valueOf(int flag) {
|
||||
for (HwmfRegionMode rm : values()) {
|
||||
if (rm.flag == flag) return rm;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -191,7 +191,7 @@ public class HwmfText {
|
|||
|
||||
@Override
|
||||
public void draw(HwmfGraphics ctx) {
|
||||
ctx.drawString(getTextBytes(), reference, null);
|
||||
ctx.drawString(getTextBytes(), reference);
|
||||
}
|
||||
|
||||
public String getText(Charset charset) {
|
||||
|
@ -396,7 +396,7 @@ public class HwmfText {
|
|||
|
||||
@Override
|
||||
public void draw(HwmfGraphics ctx) {
|
||||
ctx.drawString(rawTextBytes, reference, bounds, dx, false);
|
||||
ctx.drawString(rawTextBytes, reference, bounds, options, dx, false);
|
||||
}
|
||||
|
||||
public String getText(Charset charset) throws IOException {
|
||||
|
|
|
@ -433,6 +433,7 @@ public class HwmfWindowing {
|
|||
|
||||
@Override
|
||||
public void applyObject(HwmfGraphics ctx) {
|
||||
ctx.setClip(bounds, HwmfRegionMode.RGN_AND, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue