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@1844522 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
17cc3bab08
commit
82f118a7d7
|
@ -53,6 +53,7 @@ public class HemfGraphics extends HwmfGraphics {
|
||||||
super(graphicsCtx,bbox);
|
super(graphicsCtx,bbox);
|
||||||
// add dummy entry for object index 0, as emf is 1-based
|
// add dummy entry for object index 0, as emf is 1-based
|
||||||
objectIndexes.set(0);
|
objectIndexes.set(0);
|
||||||
|
saveTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -82,10 +83,10 @@ public class HemfGraphics extends HwmfGraphics {
|
||||||
if (tgt != null && !tgt.isEmpty()) {
|
if (tgt != null && !tgt.isEmpty()) {
|
||||||
final Rectangle2D src = bounded.getShapeBounds(this);
|
final Rectangle2D src = bounded.getShapeBounds(this);
|
||||||
if (src != null && !src.isEmpty()) {
|
if (src != null && !src.isEmpty()) {
|
||||||
graphicsCtx.translate(tgt.getCenterX() - src.getCenterX(), tgt.getCenterY() - src.getCenterY());
|
// graphicsCtx.translate(tgt.getCenterX() - src.getCenterX(), tgt.getCenterY() - src.getCenterY());
|
||||||
graphicsCtx.translate(src.getCenterX(), src.getCenterY());
|
// graphicsCtx.translate(src.getCenterX(), src.getCenterY());
|
||||||
graphicsCtx.scale(tgt.getWidth() / src.getWidth(), tgt.getHeight() / src.getHeight());
|
// graphicsCtx.scale(tgt.getWidth() / src.getWidth(), tgt.getHeight() / src.getHeight());
|
||||||
graphicsCtx.translate(-src.getCenterX(), -src.getCenterY());
|
// graphicsCtx.translate(-src.getCenterX(), -src.getCenterY());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,7 +267,27 @@ public class HemfGraphics extends HwmfGraphics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the initial AffineTransform, when this graphics context was created
|
||||||
|
*/
|
||||||
|
public AffineTransform getInitTransform() {
|
||||||
|
return new AffineTransform(transforms.peekFirst());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
/** saves the current affine transform on the stack */
|
/** saves the current affine transform on the stack */
|
||||||
private void saveTransform() {
|
private void saveTransform() {
|
||||||
|
|
|
@ -860,6 +860,11 @@ public class HemfDraw {
|
||||||
final HemfDrawProperties prop = ctx.getProperties();
|
final HemfDrawProperties prop = ctx.getProperties();
|
||||||
prop.setPath(new Path2D.Double());
|
prop.setPath(new Path2D.Double());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -876,6 +881,11 @@ public class HemfDraw {
|
||||||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -897,6 +907,11 @@ public class HemfDraw {
|
||||||
final HemfDrawProperties prop = ctx.getProperties();
|
final HemfDrawProperties prop = ctx.getProperties();
|
||||||
prop.setPath(null);
|
prop.setPath(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -929,7 +944,6 @@ public class HemfDraw {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HemfGraphics ctx) {
|
public void draw(HemfGraphics ctx) {
|
||||||
final HemfDrawProperties prop = ctx.getProperties();
|
final HemfDrawProperties prop = ctx.getProperties();
|
||||||
|
@ -940,6 +954,11 @@ public class HemfDraw {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -972,12 +991,17 @@ public class HemfDraw {
|
||||||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The EMR_STROKEPATH record renders the specified path by using the current pen.
|
* The EMR_STROKEPATH record renders the specified path by using the current pen.
|
||||||
*/
|
*/
|
||||||
public static class EmfStrokePath implements HemfRecord {
|
public static class EmfStrokePath implements HemfRecord, HemfBounded {
|
||||||
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -990,6 +1014,28 @@ public class HemfDraw {
|
||||||
// A 128-bit WMF RectL object, which specifies bounding rectangle, in device units
|
// A 128-bit WMF RectL object, which specifies bounding rectangle, in device units
|
||||||
return readRectL(leis, bounds);
|
return readRectL(leis, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(HemfGraphics ctx) {
|
||||||
|
HemfDrawProperties props = ctx.getProperties();
|
||||||
|
ctx.draw(props.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getRecordBounds() {
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getShapeBounds(HemfGraphics ctx) {
|
||||||
|
HemfDrawProperties props = ctx.getProperties();
|
||||||
|
return props.getPath().getBounds2D();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+" }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static long readRectL(LittleEndianInputStream leis, Rectangle2D bounds) {
|
static long readRectL(LittleEndianInputStream leis, Rectangle2D bounds) {
|
||||||
|
|
|
@ -358,14 +358,12 @@ public class HemfFill {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Area getShape() {
|
protected Area getShape() {
|
||||||
final Area frame = new Area();
|
return getRgnShape(rgnRects);
|
||||||
rgnRects.forEach((rct) -> frame.add(new Area(rct)));
|
|
||||||
return frame;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The EMR_INVERTRGN record inverts the colors in the specified region. */
|
/** The EMR_INVERTRGN record inverts the colors in the specified region. */
|
||||||
public static class EmfInvertRgn implements HemfRecord {
|
public static class EmfInvertRgn implements HemfRecord, HemfBounded {
|
||||||
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
||||||
protected final List<Rectangle2D> rgnRects = new ArrayList<>();
|
protected final List<Rectangle2D> rgnRects = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -383,6 +381,20 @@ public class HemfFill {
|
||||||
size += readRgnData(leis, rgnRects);
|
size += readRgnData(leis, rgnRects);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getRecordBounds() {
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getShapeBounds(HemfGraphics ctx) {
|
||||||
|
return getShape().getBounds2D();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Area getShape() {
|
||||||
|
return getRgnShape(rgnRects);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -397,7 +409,7 @@ public class HemfFill {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The EMR_FILLRGN record fills the specified region by using the specified brush. */
|
/** The EMR_FILLRGN record fills the specified region by using the specified brush. */
|
||||||
public static class EmfFillRgn extends HwmfFill.WmfFillRegion implements HemfRecord {
|
public static class EmfFillRgn extends HwmfFill.WmfFillRegion implements HemfRecord, HemfBounded {
|
||||||
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
||||||
protected final List<Rectangle2D> rgnRects = new ArrayList<>();
|
protected final List<Rectangle2D> rgnRects = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -416,6 +428,20 @@ public class HemfFill {
|
||||||
size += readRgnData(leis, rgnRects);
|
size += readRgnData(leis, rgnRects);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getRecordBounds() {
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getShapeBounds(HemfGraphics ctx) {
|
||||||
|
return getShape().getBounds2D();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Area getShape() {
|
||||||
|
return getRgnShape(rgnRects);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EmfExtSelectClipRgn implements HemfRecord {
|
public static class EmfExtSelectClipRgn implements HemfRecord {
|
||||||
|
@ -442,9 +468,13 @@ public class HemfFill {
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Area getShape() {
|
||||||
|
return getRgnShape(rgnRects);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EmfAlphaBlend implements HemfRecord {
|
public static class EmfAlphaBlend implements HemfRecord, HemfBounded {
|
||||||
/** the destination bounding rectangle in device units */
|
/** the destination bounding rectangle in device units */
|
||||||
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
||||||
/** the destination rectangle */
|
/** the destination rectangle */
|
||||||
|
@ -553,9 +583,24 @@ public class HemfFill {
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getRecordBounds() {
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getShapeBounds(HemfGraphics ctx) {
|
||||||
|
return destRect;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EmfSetDiBitsToDevice implements HemfRecord {
|
/**
|
||||||
|
* The EMR_SETDIBITSTODEVICE record specifies a block transfer of pixels from specified scanlines of
|
||||||
|
* a source bitmap to a destination rectangle.
|
||||||
|
*/
|
||||||
|
public static class EmfSetDiBitsToDevice implements HemfRecord, HemfBounded {
|
||||||
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
||||||
protected final Point2D dest = new Point2D.Double();
|
protected final Point2D dest = new Point2D.Double();
|
||||||
protected final Rectangle2D src = new Rectangle2D.Double();
|
protected final Rectangle2D src = new Rectangle2D.Double();
|
||||||
|
@ -600,6 +645,16 @@ public class HemfFill {
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getRecordBounds() {
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle2D getShapeBounds(HemfGraphics ctx) {
|
||||||
|
return new Rectangle2D.Double(dest.getX(), dest.getY(), src.getWidth(), src.getHeight());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static long readBitmap(final LittleEndianInputStream leis, final HwmfBitmapDib bitmap,
|
static long readBitmap(final LittleEndianInputStream leis, final HwmfBitmapDib bitmap,
|
||||||
|
@ -759,4 +814,10 @@ public class HemfFill {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static Area getRgnShape(List<Rectangle2D> rgnRects) {
|
||||||
|
final Area frame = new Area();
|
||||||
|
rgnRects.forEach((rct) -> frame.add(new Area(rct)));
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.poi.hemf.draw.HemfGraphics;
|
import org.apache.poi.hemf.draw.HemfGraphics;
|
||||||
|
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
|
||||||
|
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
||||||
import org.apache.poi.hwmf.record.HwmfBinaryRasterOp;
|
import org.apache.poi.hwmf.record.HwmfBinaryRasterOp;
|
||||||
import org.apache.poi.hwmf.record.HwmfBitmapDib;
|
import org.apache.poi.hwmf.record.HwmfBitmapDib;
|
||||||
import org.apache.poi.hwmf.record.HwmfBrushStyle;
|
import org.apache.poi.hwmf.record.HwmfBrushStyle;
|
||||||
|
@ -45,6 +47,44 @@ import org.apache.poi.util.LittleEndianConsts;
|
||||||
import org.apache.poi.util.LittleEndianInputStream;
|
import org.apache.poi.util.LittleEndianInputStream;
|
||||||
|
|
||||||
public class HemfMisc {
|
public class HemfMisc {
|
||||||
|
|
||||||
|
public enum HemfModifyWorldTransformMode {
|
||||||
|
/**
|
||||||
|
* Reset the current transform using the identity matrix.
|
||||||
|
* In this mode, the specified transform data is ignored.
|
||||||
|
*/
|
||||||
|
MWT_IDENTITY(1),
|
||||||
|
/**
|
||||||
|
* Multiply the current transform. In this mode, the specified transform data is the left multiplicand,
|
||||||
|
* and the transform that is currently defined in the playback device context is the right multiplicand.
|
||||||
|
*/
|
||||||
|
MWT_LEFTMULTIPLY(2),
|
||||||
|
/**
|
||||||
|
* Multiply the current transform. In this mode, the specified transform data is the right multiplicand,
|
||||||
|
* and the transform that is currently defined in the playback device context is the left multiplicand.
|
||||||
|
*/
|
||||||
|
MWT_RIGHTMULTIPLY(3),
|
||||||
|
/**
|
||||||
|
* Perform the function of an EMR_SETWORLDTRANSFORM record
|
||||||
|
*/
|
||||||
|
MWT_SET(4)
|
||||||
|
;
|
||||||
|
|
||||||
|
public final int id;
|
||||||
|
|
||||||
|
HemfModifyWorldTransformMode(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HemfModifyWorldTransformMode valueOf(int id) {
|
||||||
|
for (HemfModifyWorldTransformMode wrt : values()) {
|
||||||
|
if (wrt.id == id) return wrt;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class EmfEof implements HemfRecord {
|
public static class EmfEof implements HemfRecord {
|
||||||
protected final List<PaletteEntry> palette = new ArrayList<>();
|
protected final List<PaletteEntry> palette = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -518,6 +558,11 @@ public class HemfMisc {
|
||||||
public void draw(HemfGraphics ctx) {
|
public void draw(HemfGraphics ctx) {
|
||||||
ctx.getProperties().setPenMiterLimit(miterLimit);
|
ctx.getProperties().setPenMiterLimit(miterLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ miterLimit: "+miterLimit+" }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -552,11 +597,30 @@ public class HemfMisc {
|
||||||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
||||||
return readXForm(leis, xForm);
|
return readXForm(leis, xForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(HemfGraphics ctx) {
|
||||||
|
AffineTransform tx = ctx.getInitTransform();
|
||||||
|
tx.concatenate(xForm);
|
||||||
|
ctx.setTransform(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return
|
||||||
|
"{ xForm: " +
|
||||||
|
"{ scaleX: "+xForm.getScaleX()+
|
||||||
|
", shearX: "+xForm.getShearX()+
|
||||||
|
", transX: "+xForm.getTranslateX()+
|
||||||
|
", scaleY: "+xForm.getScaleY()+
|
||||||
|
", shearY: "+xForm.getShearY()+
|
||||||
|
", transY: "+xForm.getTranslateY()+" } }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EmfModifyWorldTransform implements HemfRecord {
|
public static class EmfModifyWorldTransform implements HemfRecord {
|
||||||
protected final AffineTransform xForm = new AffineTransform();
|
protected final AffineTransform xForm = new AffineTransform();
|
||||||
protected int modifyWorldTransformMode;
|
protected HemfModifyWorldTransformMode modifyWorldTransformMode;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HemfRecordType getEmfRecordType() {
|
public HemfRecordType getEmfRecordType() {
|
||||||
|
@ -572,16 +636,54 @@ public class HemfMisc {
|
||||||
|
|
||||||
// A 32-bit unsigned integer that specifies how the transform specified in Xform is used.
|
// A 32-bit unsigned integer that specifies how the transform specified in Xform is used.
|
||||||
// This value MUST be in the ModifyWorldTransformMode enumeration
|
// This value MUST be in the ModifyWorldTransformMode enumeration
|
||||||
modifyWorldTransformMode = (int)leis.readUInt();
|
modifyWorldTransformMode = HemfModifyWorldTransformMode.valueOf((int)leis.readUInt());
|
||||||
|
|
||||||
return size + LittleEndianConsts.INT_SIZE;
|
return size + LittleEndianConsts.INT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(HemfGraphics ctx) {
|
||||||
|
if (modifyWorldTransformMode == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (modifyWorldTransformMode) {
|
||||||
|
case MWT_IDENTITY:
|
||||||
|
ctx.setTransform(ctx.getInitTransform());
|
||||||
|
break;
|
||||||
|
case MWT_LEFTMULTIPLY: {
|
||||||
|
AffineTransform tx = new AffineTransform(xForm);
|
||||||
|
tx.concatenate(ctx.getTransform());
|
||||||
|
ctx.setTransform(tx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MWT_RIGHTMULTIPLY: {
|
||||||
|
AffineTransform tx = new AffineTransform(xForm);
|
||||||
|
tx.preConcatenate(ctx.getTransform());
|
||||||
|
ctx.setTransform(tx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
case MWT_SET: {
|
||||||
|
AffineTransform tx = ctx.getInitTransform();
|
||||||
|
tx.concatenate(xForm);
|
||||||
|
ctx.setTransform(tx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return
|
return
|
||||||
"{ xForm: { scaleX: "+xForm.getScaleX()+", shearX: "+xForm.getShearX()+", transX: "+xForm.getTranslateX()+", scaleY: "+xForm.getScaleY()+", shearY: "+xForm.getShearY()+", transY: "+xForm.getTranslateY()+" }"+
|
"{ xForm: " +
|
||||||
", modifyWorldTransformMode: "+modifyWorldTransformMode+" }";
|
"{ scaleX: "+xForm.getScaleX()+
|
||||||
|
", shearX: "+xForm.getShearX()+
|
||||||
|
", transX: "+xForm.getTranslateX()+
|
||||||
|
", scaleY: "+xForm.getScaleY()+
|
||||||
|
", shearY: "+xForm.getShearY()+
|
||||||
|
", transY: "+xForm.getTranslateY()+" }"+
|
||||||
|
", modifyWorldTransformMode: '"+modifyWorldTransformMode+"' }";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,5 +728,13 @@ public class HemfMisc {
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
|
super.applyObject(ctx);
|
||||||
|
HwmfDrawProperties props = ctx.getProperties();
|
||||||
|
props.setBrushStyle(HwmfBrushStyle.BS_PATTERN);
|
||||||
|
props.setBrushBitmap(bitmap.getImage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ public class HemfText {
|
||||||
|
|
||||||
public static class EmfExtTextOutA extends HwmfText.WmfExtTextOut implements HemfRecord {
|
public static class EmfExtTextOutA extends HwmfText.WmfExtTextOut implements HemfRecord {
|
||||||
|
|
||||||
|
protected Rectangle2D boundsIgnored = new Rectangle2D.Double();
|
||||||
protected EmfGraphicsMode graphicsMode;
|
protected EmfGraphicsMode graphicsMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,7 +82,7 @@ public class HemfText {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A WMF RectL object. It is not used and MUST be ignored on receipt.
|
// A WMF RectL object. It is not used and MUST be ignored on receipt.
|
||||||
long size = readRectL(leis, bounds);
|
long size = readRectL(leis, boundsIgnored);
|
||||||
|
|
||||||
// A 32-bit unsigned integer that specifies the graphics mode from the GraphicsMode enumeration
|
// A 32-bit unsigned integer that specifies the graphics mode from the GraphicsMode enumeration
|
||||||
graphicsMode = EmfGraphicsMode.values()[leis.readInt()-1];
|
graphicsMode = EmfGraphicsMode.values()[leis.readInt()-1];
|
||||||
|
@ -192,8 +193,7 @@ public class HemfText {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
Rectangle2D bounds = new Rectangle2D.Double(reference.getX(), reference.getY(), 0, 0);
|
ctx.drawString(rawTextBytes, reference, bounds, dx, isUnicode());
|
||||||
ctx.drawString(rawTextBytes, bounds, dx, isUnicode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -25,8 +25,11 @@ import java.awt.Paint;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
import java.awt.TexturePaint;
|
import java.awt.TexturePaint;
|
||||||
|
import java.awt.font.FontRenderContext;
|
||||||
import java.awt.font.TextAttribute;
|
import java.awt.font.TextAttribute;
|
||||||
|
import java.awt.font.TextLayout;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
@ -47,6 +50,7 @@ import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
|
||||||
import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
|
import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
|
||||||
import org.apache.poi.hwmf.record.HwmfPenStyle;
|
import org.apache.poi.hwmf.record.HwmfPenStyle;
|
||||||
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
|
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
|
||||||
|
import org.apache.poi.hwmf.record.HwmfText;
|
||||||
import org.apache.poi.sl.draw.DrawFactory;
|
import org.apache.poi.sl.draw.DrawFactory;
|
||||||
import org.apache.poi.sl.draw.DrawFontManager;
|
import org.apache.poi.sl.draw.DrawFontManager;
|
||||||
import org.apache.poi.util.LocaleUtil;
|
import org.apache.poi.util.LocaleUtil;
|
||||||
|
@ -327,11 +331,11 @@ public class HwmfGraphics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawString(byte[] text, Rectangle2D bounds) {
|
public void drawString(byte[] text, Point2D reference, Rectangle2D clip) {
|
||||||
drawString(text, bounds, null, false);
|
drawString(text, reference, clip, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawString(byte[] text, Rectangle2D bounds, List<Integer> dx, boolean isUnicode) {
|
public void drawString(byte[] text, Point2D reference, Rectangle2D clip, List<Integer> dx, boolean isUnicode) {
|
||||||
|
|
||||||
HwmfFont font = getProperties().getFont();
|
HwmfFont font = getProperties().getFont();
|
||||||
if (font == null || text == null || text.length == 0) {
|
if (font == null || text == null || text.length == 0) {
|
||||||
|
@ -396,18 +400,55 @@ public class HwmfGraphics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
double angle = Math.toRadians(-font.getEscapement()/10.);
|
double angle = Math.toRadians(-font.getEscapement()/10.);
|
||||||
|
|
||||||
|
final HwmfText.HwmfTextAlignment align = prop.getTextAlignLatin();
|
||||||
|
final HwmfText.HwmfTextVerticalAlignment valign = prop.getTextVAlignLatin();
|
||||||
|
final FontRenderContext frc = graphicsCtx.getFontRenderContext();
|
||||||
|
final TextLayout layout = new TextLayout(as.getIterator(), frc);
|
||||||
|
|
||||||
|
final Rectangle2D pixelBounds = layout.getBounds();
|
||||||
|
|
||||||
|
AffineTransform tx = new AffineTransform();
|
||||||
|
switch (align) {
|
||||||
|
default:
|
||||||
|
case LEFT:
|
||||||
|
break;
|
||||||
|
case CENTER:
|
||||||
|
tx.translate(-pixelBounds.getWidth() / 2., 0);
|
||||||
|
break;
|
||||||
|
case RIGHT:
|
||||||
|
tx.translate(-pixelBounds.getWidth(), 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check min/max orientation
|
||||||
|
switch (valign) {
|
||||||
|
case TOP:
|
||||||
|
tx.translate(0, layout.getAscent());
|
||||||
|
default:
|
||||||
|
case BASELINE:
|
||||||
|
break;
|
||||||
|
case BOTTOM:
|
||||||
|
tx.translate(0, pixelBounds.getHeight());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tx.rotate(angle);
|
||||||
|
Point2D src = new Point2D.Double();
|
||||||
|
Point2D dst = new Point2D.Double();
|
||||||
|
tx.transform(src, dst);
|
||||||
|
|
||||||
|
// TODO: implement clipping on bounds
|
||||||
final AffineTransform at = graphicsCtx.getTransform();
|
final AffineTransform at = graphicsCtx.getTransform();
|
||||||
try {
|
try {
|
||||||
graphicsCtx.translate(bounds.getX(), bounds.getY());
|
graphicsCtx.translate(reference.getX(), reference.getY());
|
||||||
graphicsCtx.rotate(angle);
|
graphicsCtx.rotate(angle);
|
||||||
graphicsCtx.translate(0, fontH);
|
graphicsCtx.translate(dst.getX(), dst.getY());
|
||||||
if (getProperties().getBkMode() == HwmfBkMode.OPAQUE) {
|
if (getProperties().getBkMode() == HwmfBkMode.OPAQUE && clip != null) {
|
||||||
// TODO: validate bounds
|
// TODO: validate bounds
|
||||||
graphicsCtx.setBackground(getProperties().getBackgroundColor().getColor());
|
graphicsCtx.setBackground(getProperties().getBackgroundColor().getColor());
|
||||||
graphicsCtx.fill(new Rectangle2D.Double(0, 0, bounds.getWidth(), bounds.getHeight()));
|
graphicsCtx.fill(new Rectangle2D.Double(0, 0, clip.getWidth(), clip.getHeight()));
|
||||||
}
|
}
|
||||||
graphicsCtx.setColor(getProperties().getTextColor().getColor());
|
graphicsCtx.setColor(getProperties().getTextColor().getColor());
|
||||||
graphicsCtx.drawString(as.getIterator(), 0, 0);
|
graphicsCtx.drawString(as.getIterator(), 0, 0);
|
||||||
|
@ -415,11 +456,11 @@ public class HwmfGraphics {
|
||||||
graphicsCtx.setTransform(at);
|
graphicsCtx.setTransform(at);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAttributes(AttributedString as, HwmfFont font) {
|
private void addAttributes(AttributedString as, HwmfFont font) {
|
||||||
DrawFontManager fontHandler = DrawFactory.getInstance(graphicsCtx).getFontManager(graphicsCtx);
|
DrawFontManager fontHandler = DrawFactory.getInstance(graphicsCtx).getFontManager(graphicsCtx);
|
||||||
FontInfo fontInfo = fontHandler.getMappedFont(graphicsCtx, font);
|
FontInfo fontInfo = fontHandler.getMappedFont(graphicsCtx, font);
|
||||||
|
|
||||||
as.addAttribute(TextAttribute.FAMILY, fontInfo.getTypeface());
|
as.addAttribute(TextAttribute.FAMILY, fontInfo.getTypeface());
|
||||||
as.addAttribute(TextAttribute.SIZE, getFontHeight(font));
|
as.addAttribute(TextAttribute.SIZE, getFontHeight(font));
|
||||||
if (font.isStrikeOut()) {
|
if (font.isStrikeOut()) {
|
||||||
|
|
|
@ -374,6 +374,16 @@ public class HwmfDraw {
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
ctx.fill(bounds);
|
ctx.fill(bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return
|
||||||
|
"{ bounds: " +
|
||||||
|
"{ x: "+bounds.getX()+
|
||||||
|
", y: "+bounds.getY()+
|
||||||
|
", w: "+bounds.getWidth()+
|
||||||
|
", h: "+bounds.getHeight()+" } }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -302,6 +302,11 @@ public class HwmfMisc {
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ drawMode: '"+drawMode+"' }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -164,17 +164,9 @@ public class HwmfText {
|
||||||
* The string is written at the location specified by the XStart and YStart fields.
|
* The string is written at the location specified by the XStart and YStart fields.
|
||||||
*/
|
*/
|
||||||
private byte[] rawTextBytes;
|
private byte[] rawTextBytes;
|
||||||
/**
|
|
||||||
* A 16-bit signed integer that defines the vertical (y-axis) coordinate, in logical
|
protected Point2D reference = new Point2D.Double();
|
||||||
* units, of the point where drawing is to start.
|
|
||||||
*/
|
|
||||||
private int yStart;
|
|
||||||
/**
|
|
||||||
* A 16-bit signed integer that defines the horizontal (x-axis) coordinate, in
|
|
||||||
* logical units, of the point where drawing is to start.
|
|
||||||
*/
|
|
||||||
private int xStart;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HwmfRecordType getWmfRecordType() {
|
public HwmfRecordType getWmfRecordType() {
|
||||||
return HwmfRecordType.textOut;
|
return HwmfRecordType.textOut;
|
||||||
|
@ -185,15 +177,19 @@ public class HwmfText {
|
||||||
stringLength = leis.readShort();
|
stringLength = leis.readShort();
|
||||||
rawTextBytes = IOUtils.safelyAllocate(stringLength+(stringLength&1), MAX_RECORD_LENGTH);
|
rawTextBytes = IOUtils.safelyAllocate(stringLength+(stringLength&1), MAX_RECORD_LENGTH);
|
||||||
leis.readFully(rawTextBytes);
|
leis.readFully(rawTextBytes);
|
||||||
yStart = leis.readShort();
|
// A 16-bit signed integer that defines the vertical (y-axis) coordinate, in logical
|
||||||
xStart = leis.readShort();
|
// units, of the point where drawing is to start.
|
||||||
|
int yStart = leis.readShort();
|
||||||
|
// A 16-bit signed integer that defines the horizontal (x-axis) coordinate, in
|
||||||
|
// logical units, of the point where drawing is to start.
|
||||||
|
int xStart = leis.readShort();
|
||||||
|
reference.setLocation(xStart, yStart);
|
||||||
return 3*LittleEndianConsts.SHORT_SIZE+rawTextBytes.length;
|
return 3*LittleEndianConsts.SHORT_SIZE+rawTextBytes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
Rectangle2D bounds = new Rectangle2D.Double(xStart, yStart, 0, 0);
|
ctx.drawString(getTextBytes(), reference, null);
|
||||||
ctx.drawString(getTextBytes(), bounds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText(Charset charset) {
|
public String getText(Charset charset) {
|
||||||
|
@ -398,8 +394,7 @@ public class HwmfText {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
Rectangle2D bounds = new Rectangle2D.Double(reference.getX(), reference.getY(), 0, 0);
|
ctx.drawString(rawTextBytes, reference, bounds, dx, false);
|
||||||
ctx.drawString(rawTextBytes, bounds, dx, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText(Charset charset) throws IOException {
|
public String getText(Charset charset) throws IOException {
|
||||||
|
|
|
@ -63,7 +63,8 @@ public class HemfPictureTest {
|
||||||
public void paint() throws IOException {
|
public void paint() throws IOException {
|
||||||
byte buf[] = new byte[50_000_000];
|
byte buf[] = new byte[50_000_000];
|
||||||
|
|
||||||
final boolean writeLog = true;
|
final boolean writeLog = false;
|
||||||
|
final boolean dumpRecords = false;
|
||||||
final boolean savePng = true;
|
final boolean savePng = true;
|
||||||
|
|
||||||
Set<String> passed = new HashSet<>();
|
Set<String> passed = new HashSet<>();
|
||||||
|
@ -101,6 +102,10 @@ public class HemfPictureTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dumpRecords) {
|
||||||
|
dumpRecords(emf);
|
||||||
|
}
|
||||||
|
|
||||||
Graphics2D g = null;
|
Graphics2D g = null;
|
||||||
try {
|
try {
|
||||||
Dimension2D dim = emf.getSize();
|
Dimension2D dim = emf.getSize();
|
||||||
|
@ -112,17 +117,23 @@ public class HemfPictureTest {
|
||||||
width *= 1500. / max;
|
width *= 1500. / max;
|
||||||
height *= 1500. / max;
|
height *= 1500. / max;
|
||||||
}
|
}
|
||||||
|
width = Math.ceil(width);
|
||||||
|
height = Math.ceil(height);
|
||||||
|
|
||||||
BufferedImage bufImg = new BufferedImage((int)Math.ceil(width), (int)Math.ceil(height), BufferedImage.TYPE_INT_ARGB);
|
BufferedImage bufImg = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB);
|
||||||
g = bufImg.createGraphics();
|
g = bufImg.createGraphics();
|
||||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||||
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
||||||
|
|
||||||
|
g.setComposite(AlphaComposite.Clear);
|
||||||
|
g.fillRect(0, 0, (int)width, (int)height);
|
||||||
|
g.setComposite(AlphaComposite.Src);
|
||||||
|
|
||||||
emf.draw(g, new Rectangle2D.Double(0, 0, width, height));
|
emf.draw(g, new Rectangle2D.Double(0, 0, width, height));
|
||||||
|
|
||||||
final File pngName = new File("build/tmp", etName.replaceFirst(".*"+"/", "").replace(".emf", ".png"));
|
final File pngName = new File("build/tmp", etName.replaceFirst(".+/", "").replace(".emf", ".png"));
|
||||||
if (savePng) {
|
if (savePng) {
|
||||||
ImageIO.write(bufImg, "PNG", pngName);
|
ImageIO.write(bufImg, "PNG", pngName);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue