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@1843342 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
616f8127cf
commit
f927e551cc
|
@ -17,6 +17,10 @@
|
|||
|
||||
package org.apache.poi.hemf.draw;
|
||||
|
||||
import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_NULL;
|
||||
import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_SOLID;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Path2D;
|
||||
|
@ -29,11 +33,20 @@ import java.util.function.Consumer;
|
|||
import org.apache.poi.hemf.record.emf.HemfBounded;
|
||||
import org.apache.poi.hemf.record.emf.HemfRecord;
|
||||
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
||||
import org.apache.poi.hwmf.record.HwmfColorRef;
|
||||
import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
|
||||
import org.apache.poi.hwmf.record.HwmfPenStyle;
|
||||
import org.apache.poi.util.Internal;
|
||||
|
||||
public class HemfGraphics extends HwmfGraphics {
|
||||
|
||||
private static final HwmfColorRef WHITE = new HwmfColorRef(Color.WHITE);
|
||||
private static final HwmfColorRef LTGRAY = new HwmfColorRef(new Color(0x00C0C0C0));
|
||||
private static final HwmfColorRef GRAY = new HwmfColorRef(new Color(0x00808080));
|
||||
private static final HwmfColorRef DKGRAY = new HwmfColorRef(new Color(0x00404040));
|
||||
private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK);
|
||||
|
||||
|
||||
private final Deque<AffineTransform> transforms = new ArrayDeque<>();
|
||||
|
||||
public HemfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {
|
||||
|
@ -52,8 +65,7 @@ public class HemfGraphics extends HwmfGraphics {
|
|||
|
||||
@Override
|
||||
public void saveProperties() {
|
||||
assert(prop != null);
|
||||
propStack.add(prop);
|
||||
propStack.add(getProperties());
|
||||
prop = new HemfDrawProperties((HemfDrawProperties)prop);
|
||||
}
|
||||
|
||||
|
@ -138,6 +150,125 @@ public class HemfGraphics extends HwmfGraphics {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyObjectTableEntry(int index) {
|
||||
if ((index & 0x80000000) != 0) {
|
||||
selectStockObject(index);
|
||||
} else {
|
||||
super.applyObjectTableEntry(index);
|
||||
}
|
||||
}
|
||||
|
||||
private void selectStockObject(int objectIndex) {
|
||||
final HemfDrawProperties prop = getProperties();
|
||||
switch (objectIndex) {
|
||||
case 0x80000000:
|
||||
// WHITE_BRUSH - A white, solid-color brush
|
||||
// BrushStyle: BS_SOLID
|
||||
// Color: 0x00FFFFFF
|
||||
prop.setBrushColor(WHITE);
|
||||
prop.setBrushStyle(BS_SOLID);
|
||||
break;
|
||||
case 0x80000001:
|
||||
// LTGRAY_BRUSH - A light gray, solid-color brush
|
||||
// BrushStyle: BS_SOLID
|
||||
// Color: 0x00C0C0C0
|
||||
prop.setBrushColor(LTGRAY);
|
||||
prop.setBrushStyle(BS_SOLID);
|
||||
break;
|
||||
case 0x80000002:
|
||||
// GRAY_BRUSH - A gray, solid-color brush
|
||||
// BrushStyle: BS_SOLID
|
||||
// Color: 0x00808080
|
||||
prop.setBrushColor(GRAY);
|
||||
prop.setBrushStyle(BS_SOLID);
|
||||
break;
|
||||
case 0x80000003:
|
||||
// DKGRAY_BRUSH - A dark gray, solid color brush
|
||||
// BrushStyle: BS_SOLID
|
||||
// Color: 0x00404040
|
||||
prop.setBrushColor(DKGRAY);
|
||||
prop.setBrushStyle(BS_SOLID);
|
||||
break;
|
||||
case 0x80000004:
|
||||
// BLACK_BRUSH - A black, solid color brush
|
||||
// BrushStyle: BS_SOLID
|
||||
// Color: 0x00000000
|
||||
prop.setBrushColor(BLACK);
|
||||
prop.setBrushStyle(BS_SOLID);
|
||||
break;
|
||||
case 0x80000005:
|
||||
// NULL_BRUSH - A null brush
|
||||
// BrushStyle: BS_NULL
|
||||
prop.setBrushStyle(BS_NULL);
|
||||
break;
|
||||
case 0x80000006:
|
||||
// WHITE_PEN - A white, solid-color pen
|
||||
// PenStyle: PS_COSMETIC + PS_SOLID
|
||||
// ColorRef: 0x00FFFFFF
|
||||
prop.setPenStyle(HwmfPenStyle.valueOf(0));
|
||||
prop.setPenWidth(1);
|
||||
prop.setPenColor(WHITE);
|
||||
break;
|
||||
case 0x80000007:
|
||||
// BLACK_PEN - A black, solid-color pen
|
||||
// PenStyle: PS_COSMETIC + PS_SOLID
|
||||
// ColorRef: 0x00000000
|
||||
prop.setPenStyle(HwmfPenStyle.valueOf(0));
|
||||
prop.setPenWidth(1);
|
||||
prop.setPenColor(BLACK);
|
||||
break;
|
||||
case 0x80000008:
|
||||
// NULL_PEN - A null pen
|
||||
// PenStyle: PS_NULL
|
||||
prop.setPenStyle(HwmfPenStyle.valueOf(HwmfPenStyle.HwmfLineDash.NULL.wmfFlag));
|
||||
break;
|
||||
case 0x8000000A:
|
||||
// OEM_FIXED_FONT - A fixed-width, OEM character set
|
||||
// Charset: OEM_CHARSET
|
||||
// PitchAndFamily: FF_DONTCARE + FIXED_PITCH
|
||||
break;
|
||||
case 0x8000000B:
|
||||
// ANSI_FIXED_FONT - A fixed-width font
|
||||
// Charset: ANSI_CHARSET
|
||||
// PitchAndFamily: FF_DONTCARE + FIXED_PITCH
|
||||
break;
|
||||
case 0x8000000C:
|
||||
// ANSI_VAR_FONT - A variable-width font
|
||||
// Charset: ANSI_CHARSET
|
||||
// PitchAndFamily: FF_DONTCARE + VARIABLE_PITCH
|
||||
break;
|
||||
case 0x8000000D:
|
||||
// SYSTEM_FONT - A font that is guaranteed to be available in the operating system
|
||||
break;
|
||||
case 0x8000000E:
|
||||
// DEVICE_DEFAULT_FONT
|
||||
// The default font that is provided by the graphics device driver for the current output device
|
||||
break;
|
||||
case 0x8000000F:
|
||||
// DEFAULT_PALETTE
|
||||
// The default palette that is defined for the current output device.
|
||||
break;
|
||||
case 0x80000010:
|
||||
// SYSTEM_FIXED_FONT
|
||||
// A fixed-width font that is guaranteed to be available in the operating system.
|
||||
break;
|
||||
case 0x80000011:
|
||||
// DEFAULT_GUI_FONT
|
||||
// The default font that is used for user interface objects such as menus and dialog boxes.
|
||||
break;
|
||||
case 0x80000012:
|
||||
// DC_BRUSH
|
||||
// The solid-color brush that is currently selected in the playback device context.
|
||||
break;
|
||||
case 0x80000013:
|
||||
// DC_PEN
|
||||
// The solid-color pen that is currently selected in the playback device context.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** saves the current affine transform on the stack */
|
||||
private void saveTransform() {
|
||||
|
|
|
@ -40,7 +40,7 @@ import org.apache.poi.util.RecordFormatException;
|
|||
*/
|
||||
@Internal
|
||||
public class HemfComment {
|
||||
private static final int MAX_RECORD_LENGTH = 1_000_000;
|
||||
private static final int MAX_RECORD_LENGTH = 2_000_000;
|
||||
|
||||
public enum HemfCommentRecordType {
|
||||
emfGeneric(-1, EmfCommentDataGeneric::new, false),
|
||||
|
|
|
@ -47,12 +47,6 @@ public class HemfDraw {
|
|||
*/
|
||||
public static class EmfSelectObject extends WmfSelectObject implements HemfRecord {
|
||||
|
||||
private static final HwmfColorRef WHITE = new HwmfColorRef(Color.WHITE);
|
||||
private static final HwmfColorRef LTGRAY = new HwmfColorRef(new Color(0x00C0C0C0));
|
||||
private static final HwmfColorRef GRAY = new HwmfColorRef(new Color(0x00808080));
|
||||
private static final HwmfColorRef DKGRAY = new HwmfColorRef(new Color(0x00404040));
|
||||
private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK);
|
||||
|
||||
private static final String[] STOCK_IDS = {
|
||||
"0x80000000 /* WHITE_BRUSH */",
|
||||
"0x80000001 /* LTGRAY_BRUSH */",
|
||||
|
@ -88,124 +82,6 @@ public class HemfDraw {
|
|||
return LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(HemfGraphics ctx) {
|
||||
if ((objectIndex & 0x80000000) != 0) {
|
||||
selectStockObject(ctx);
|
||||
} else {
|
||||
super.draw(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
private void selectStockObject(HemfGraphics ctx) {
|
||||
final HemfDrawProperties prop = ctx.getProperties();
|
||||
switch (objectIndex) {
|
||||
case 0x80000000:
|
||||
// WHITE_BRUSH - A white, solid-color brush
|
||||
// BrushStyle: BS_SOLID
|
||||
// Color: 0x00FFFFFF
|
||||
prop.setBrushColor(WHITE);
|
||||
prop.setBrushStyle(BS_SOLID);
|
||||
break;
|
||||
case 0x80000001:
|
||||
// LTGRAY_BRUSH - A light gray, solid-color brush
|
||||
// BrushStyle: BS_SOLID
|
||||
// Color: 0x00C0C0C0
|
||||
prop.setBrushColor(LTGRAY);
|
||||
prop.setBrushStyle(BS_SOLID);
|
||||
break;
|
||||
case 0x80000002:
|
||||
// GRAY_BRUSH - A gray, solid-color brush
|
||||
// BrushStyle: BS_SOLID
|
||||
// Color: 0x00808080
|
||||
prop.setBrushColor(GRAY);
|
||||
prop.setBrushStyle(BS_SOLID);
|
||||
break;
|
||||
case 0x80000003:
|
||||
// DKGRAY_BRUSH - A dark gray, solid color brush
|
||||
// BrushStyle: BS_SOLID
|
||||
// Color: 0x00404040
|
||||
prop.setBrushColor(DKGRAY);
|
||||
prop.setBrushStyle(BS_SOLID);
|
||||
break;
|
||||
case 0x80000004:
|
||||
// BLACK_BRUSH - A black, solid color brush
|
||||
// BrushStyle: BS_SOLID
|
||||
// Color: 0x00000000
|
||||
prop.setBrushColor(BLACK);
|
||||
prop.setBrushStyle(BS_SOLID);
|
||||
break;
|
||||
case 0x80000005:
|
||||
// NULL_BRUSH - A null brush
|
||||
// BrushStyle: BS_NULL
|
||||
prop.setBrushStyle(BS_NULL);
|
||||
break;
|
||||
case 0x80000006:
|
||||
// WHITE_PEN - A white, solid-color pen
|
||||
// PenStyle: PS_COSMETIC + PS_SOLID
|
||||
// ColorRef: 0x00FFFFFF
|
||||
prop.setPenStyle(HwmfPenStyle.valueOf(0));
|
||||
prop.setPenWidth(1);
|
||||
prop.setPenColor(WHITE);
|
||||
break;
|
||||
case 0x80000007:
|
||||
// BLACK_PEN - A black, solid-color pen
|
||||
// PenStyle: PS_COSMETIC + PS_SOLID
|
||||
// ColorRef: 0x00000000
|
||||
prop.setPenStyle(HwmfPenStyle.valueOf(0));
|
||||
prop.setPenWidth(1);
|
||||
prop.setPenColor(BLACK);
|
||||
break;
|
||||
case 0x80000008:
|
||||
// NULL_PEN - A null pen
|
||||
// PenStyle: PS_NULL
|
||||
prop.setPenStyle(HwmfPenStyle.valueOf(HwmfPenStyle.HwmfLineDash.NULL.wmfFlag));
|
||||
break;
|
||||
case 0x8000000A:
|
||||
// OEM_FIXED_FONT - A fixed-width, OEM character set
|
||||
// Charset: OEM_CHARSET
|
||||
// PitchAndFamily: FF_DONTCARE + FIXED_PITCH
|
||||
break;
|
||||
case 0x8000000B:
|
||||
// ANSI_FIXED_FONT - A fixed-width font
|
||||
// Charset: ANSI_CHARSET
|
||||
// PitchAndFamily: FF_DONTCARE + FIXED_PITCH
|
||||
break;
|
||||
case 0x8000000C:
|
||||
// ANSI_VAR_FONT - A variable-width font
|
||||
// Charset: ANSI_CHARSET
|
||||
// PitchAndFamily: FF_DONTCARE + VARIABLE_PITCH
|
||||
break;
|
||||
case 0x8000000D:
|
||||
// SYSTEM_FONT - A font that is guaranteed to be available in the operating system
|
||||
break;
|
||||
case 0x8000000E:
|
||||
// DEVICE_DEFAULT_FONT
|
||||
// The default font that is provided by the graphics device driver for the current output device
|
||||
break;
|
||||
case 0x8000000F:
|
||||
// DEFAULT_PALETTE
|
||||
// The default palette that is defined for the current output device.
|
||||
break;
|
||||
case 0x80000010:
|
||||
// SYSTEM_FIXED_FONT
|
||||
// A fixed-width font that is guaranteed to be available in the operating system.
|
||||
break;
|
||||
case 0x80000011:
|
||||
// DEFAULT_GUI_FONT
|
||||
// The default font that is used for user interface objects such as menus and dialog boxes.
|
||||
break;
|
||||
case 0x80000012:
|
||||
// DC_BRUSH
|
||||
// The solid-color brush that is currently selected in the playback device context.
|
||||
break;
|
||||
case 0x80000013:
|
||||
// DC_PEN
|
||||
// The solid-color pen that is currently selected in the playback device context.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{ index: "+
|
||||
|
@ -1166,6 +1042,11 @@ public class HemfDraw {
|
|||
|
||||
private static void polyTo(final HemfGraphics ctx, final Path2D poly) {
|
||||
final PathIterator pi = poly.getPathIterator(null);
|
||||
if (pi.isDone()) {
|
||||
// ignore empty polys
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore dummy start point (moveTo)
|
||||
pi.next();
|
||||
assert (!pi.isDone());
|
||||
|
|
|
@ -19,6 +19,7 @@ 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 java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Area;
|
||||
|
@ -100,7 +101,7 @@ public class HemfFill {
|
|||
@Override
|
||||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
||||
long size = readPointL(leis, start);
|
||||
size = colorRef.init(leis);
|
||||
size += colorRef.init(leis);
|
||||
// A 32-bit unsigned integer that specifies how to use the Color value to determine the area for
|
||||
// the flood fill operation. The value MUST be in the FloodFill enumeration
|
||||
mode = (int)leis.readUInt();
|
||||
|
@ -117,7 +118,7 @@ public class HemfFill {
|
|||
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. */
|
||||
protected final byte[] xformSrc = new byte[24];
|
||||
protected final AffineTransform xFormSrc = new AffineTransform();
|
||||
|
||||
/** A WMF ColorRef object that specifies the background color of the source bitmap. */
|
||||
protected final HwmfColorRef bkColorSrc = new HwmfColorRef();
|
||||
|
@ -155,8 +156,7 @@ public class HemfFill {
|
|||
final Point2D srcPnt = new Point2D.Double();
|
||||
size += readPointL(leis, srcPnt);
|
||||
|
||||
leis.readFully(xformSrc);
|
||||
size += 24;
|
||||
size += readXForm(leis, xFormSrc);
|
||||
|
||||
size += bkColorSrc.init(leis);
|
||||
|
||||
|
@ -168,6 +168,10 @@ 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) {
|
||||
return size;
|
||||
}
|
||||
|
||||
// A 32-bit unsigned integer that specifies the offset, in bytes, from the
|
||||
// start of this record to the source bitmap bits in the BitmapBuffer field.
|
||||
|
@ -176,7 +180,7 @@ 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 += 5*LittleEndianConsts.INT_SIZE;
|
||||
size += 2*LittleEndianConsts.INT_SIZE;
|
||||
|
||||
if (srcEqualsDstDimension()) {
|
||||
srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight());
|
||||
|
@ -205,6 +209,16 @@ public class HemfFill {
|
|||
protected boolean srcEqualsDstDimension() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return
|
||||
"{ bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+"}"+
|
||||
", 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -589,18 +603,24 @@ public class HemfFill {
|
|||
}
|
||||
|
||||
static long readBitmap(final LittleEndianInputStream leis, final HwmfBitmapDib bitmap,
|
||||
final int startIdx, final int offBmiSrc, final int cbBmiSrc, final int offBitsSrc, int cbBitsSrc)
|
||||
final int startIdx, final int offBmi, final int cbBmi, final int offBits, int cbBits)
|
||||
throws IOException {
|
||||
final int offCurr = leis.getReadIndex()-startIdx;
|
||||
final int undefinedSpace1 = offBmiSrc-offCurr;
|
||||
assert(undefinedSpace1 >= 0);
|
||||
if (offBmi == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final int undefinedSpace2 = offBitsSrc-offCurr-cbBmiSrc-undefinedSpace1;
|
||||
final int offCurr = leis.getReadIndex()-(startIdx-HEADER_SIZE);
|
||||
final int undefinedSpace1 = offBmi-offCurr;
|
||||
if (undefinedSpace1 < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final int undefinedSpace2 = offBits-offCurr-cbBmi-undefinedSpace1;
|
||||
assert(undefinedSpace2 >= 0);
|
||||
|
||||
leis.skipFully(undefinedSpace1);
|
||||
|
||||
if (cbBmiSrc == 0 || cbBitsSrc == 0) {
|
||||
if (cbBmi == 0 || cbBits == 0) {
|
||||
return undefinedSpace1;
|
||||
}
|
||||
|
||||
|
@ -608,18 +628,18 @@ public class HemfFill {
|
|||
if (undefinedSpace2 == 0) {
|
||||
leisDib = leis;
|
||||
} else {
|
||||
final ByteArrayOutputStream bos = new ByteArrayOutputStream(cbBmiSrc+cbBitsSrc);
|
||||
final long cbBmiSrcAct = IOUtils.copy(leis, bos, cbBmiSrc);
|
||||
assert (cbBmiSrcAct == cbBmiSrc);
|
||||
final ByteArrayOutputStream bos = new ByteArrayOutputStream(cbBmi+cbBits);
|
||||
final long cbBmiSrcAct = IOUtils.copy(leis, bos, cbBmi);
|
||||
assert (cbBmiSrcAct == cbBmi);
|
||||
leis.skipFully(undefinedSpace2);
|
||||
final long cbBitsSrcAct = IOUtils.copy(leis, bos, cbBitsSrc);
|
||||
assert (cbBitsSrcAct == cbBitsSrc);
|
||||
final long cbBitsSrcAct = IOUtils.copy(leis, bos, cbBits);
|
||||
assert (cbBitsSrcAct == cbBits);
|
||||
leisDib = new LittleEndianInputStream(new ByteArrayInputStream(bos.toByteArray()));
|
||||
}
|
||||
final int dibSize = cbBmiSrc+cbBitsSrc;
|
||||
final int dibSize = cbBmi+cbBits;
|
||||
final int dibSizeAct = bitmap.init(leisDib, dibSize);
|
||||
assert (dibSizeAct <= dibSize);
|
||||
return undefinedSpace1 + cbBmiSrc + undefinedSpace2 + cbBitsSrc;
|
||||
return undefinedSpace1 + cbBmi + undefinedSpace2 + cbBits;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.apache.poi.hwmf.record.HwmfBinaryRasterOp;
|
|||
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.HwmfFill;
|
||||
import org.apache.poi.hwmf.record.HwmfHatchStyle;
|
||||
import org.apache.poi.hwmf.record.HwmfMapMode;
|
||||
import org.apache.poi.hwmf.record.HwmfMisc;
|
||||
|
@ -65,8 +66,8 @@ public class HemfMisc {
|
|||
|
||||
int size = 2 * LittleEndianConsts.INT_SIZE;
|
||||
|
||||
if (offPalEntries > 0) {
|
||||
int undefinedSpace1 = (int) (offPalEntries - size - HEADER_SIZE);
|
||||
if (nPalEntries > 0 && offPalEntries > 0) {
|
||||
int undefinedSpace1 = (int) (offPalEntries - (size + HEADER_SIZE));
|
||||
assert (undefinedSpace1 >= 0);
|
||||
leis.skipFully(undefinedSpace1);
|
||||
size += undefinedSpace1;
|
||||
|
@ -283,6 +284,59 @@ public class HemfMisc {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The EMR_CREATEDIBPATTERNBRUSHPT record defines a pattern brush for graphics operations.
|
||||
* The pattern is specified by a DIB.
|
||||
*/
|
||||
public static class EmfCreateDibPatternBrushPt extends HwmfMisc.WmfDibCreatePatternBrush implements HemfRecord {
|
||||
protected int brushIdx;
|
||||
|
||||
@Override
|
||||
public HemfRecordType getEmfRecordType() {
|
||||
return HemfRecordType.createDibPatternBrushPt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
||||
final int startIdx = leis.getReadIndex();
|
||||
|
||||
style = HwmfBrushStyle.BS_DIBPATTERNPT;
|
||||
|
||||
// A 32-bit unsigned integer that specifies the index of the pattern brush
|
||||
// object in the EMF Object Table
|
||||
brushIdx = (int)leis.readUInt();
|
||||
|
||||
// A 32-bit unsigned integer that specifies how to interpret values in the color
|
||||
// table in the DIB header. This value MUST be in the DIBColors enumeration
|
||||
colorUsage = HwmfFill.ColorUsage.valueOf((int)leis.readUInt());
|
||||
|
||||
// A 32-bit unsigned integer that specifies the offset from the start of this
|
||||
// record to the DIB header.
|
||||
final int offBmi = leis.readInt();
|
||||
|
||||
// A 32-bit unsigned integer that specifies the size of the DIB header.
|
||||
final int cbBmi = leis.readInt();
|
||||
|
||||
// A 32-bit unsigned integer that specifies the offset from the start of this record to the DIB bits.
|
||||
final int offBits = leis.readInt();
|
||||
|
||||
// A 32-bit unsigned integer that specifies the size of the DIB bits.
|
||||
final int cbBits = leis.readInt();
|
||||
|
||||
int size = 6*LittleEndianConsts.INT_SIZE;
|
||||
|
||||
patternDib = new HwmfBitmapDib();
|
||||
size += readBitmap(leis, patternDib, startIdx, offBmi, cbBmi, offBits, cbBits);
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(HemfGraphics ctx) {
|
||||
ctx.addObjectTableEntry(this, brushIdx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The EMR_DELETEOBJECT record deletes a graphics object, which is specified by its index
|
||||
* in the EMF Object Table
|
||||
|
@ -519,5 +573,12 @@ public class HemfMisc {
|
|||
|
||||
return size + LittleEndianConsts.INT_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return
|
||||
"{ xForm: { scaleX: "+xForm.getScaleX()+", shearX: "+xForm.getShearX()+", transX: "+xForm.getTranslateX()+", scaleY: "+xForm.getScaleY()+", shearY: "+xForm.getShearY()+", transY: "+xForm.getTranslateY()+" }"+
|
||||
", modifyWorldTransformMode: "+modifyWorldTransformMode+" }";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ public enum HemfRecordType {
|
|||
polyPolygon16(0x0000005B, HemfDraw.EmfPolyPolygon16::new),
|
||||
polyDraw16(0x0000005C, HemfDraw.EmfPolyDraw16::new),
|
||||
createmonobrush16(0x0000005D, UnimplementedHemfRecord::new),
|
||||
createdibpatternbrushpt(0x0000005E, UnimplementedHemfRecord::new),
|
||||
createDibPatternBrushPt(0x0000005E, HemfMisc.EmfCreateDibPatternBrushPt::new),
|
||||
extCreatePen(0x0000005F, HemfMisc.EmfExtCreatePen::new),
|
||||
polytextouta(0x00000060, HemfText.PolyTextOutA::new),
|
||||
polytextoutw(0x00000061, HemfText.PolyTextOutW::new),
|
||||
|
|
|
@ -113,7 +113,7 @@ public class HemfText {
|
|||
int offDx = (int)leis.readUInt();
|
||||
size += LittleEndianConsts.INT_SIZE;
|
||||
|
||||
int undefinedSpace1 = (int)(offString - size - HEADER_SIZE);
|
||||
int undefinedSpace1 = (int)(offString - (size + HEADER_SIZE));
|
||||
assert (undefinedSpace1 >= 0);
|
||||
leis.skipFully(undefinedSpace1);
|
||||
size += undefinedSpace1;
|
||||
|
@ -124,7 +124,7 @@ public class HemfText {
|
|||
|
||||
dx.clear();
|
||||
if (offDx > 0) {
|
||||
int undefinedSpace2 = (int) (offDx - size - HEADER_SIZE);
|
||||
int undefinedSpace2 = (int) (offDx - (size + HEADER_SIZE));
|
||||
assert (undefinedSpace2 >= 0);
|
||||
leis.skipFully(undefinedSpace2);
|
||||
size += undefinedSpace2;
|
||||
|
@ -149,10 +149,6 @@ public class HemfText {
|
|||
return size;
|
||||
}
|
||||
|
||||
protected boolean isUnicode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* To be implemented! We need to get the current character set
|
||||
|
@ -185,21 +181,10 @@ public class HemfText {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
String text = "";
|
||||
try {
|
||||
text = getText(isUnicode() ? Charsets.UTF_16LE : LocaleUtil.CHARSET_1252);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
|
||||
return
|
||||
"{ bounds: { x: "+bounds.getX()+
|
||||
", y: "+bounds.getY()+
|
||||
", w: "+bounds.getWidth()+
|
||||
", h: "+bounds.getHeight()+
|
||||
"}, graphicsMode: '"+graphicsMode+"'"+
|
||||
", scale: { w: "+scale.getWidth()+", h: "+scale.getHeight()+" }"+
|
||||
", text: '"+text.replaceAll("\\p{Cntrl}",".")+"'"+
|
||||
"}";
|
||||
"{ graphicsMode: '"+graphicsMode+"'"+
|
||||
", scale: { w: "+scale.getWidth()+", h: "+scale.getHeight()+" },"+
|
||||
super.toString().substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,11 +108,11 @@ public class HemfPicture implements Iterable<HemfRecord> {
|
|||
ctx.scale(graphicsBounds.getWidth()/emfBounds.getWidth(), graphicsBounds.getHeight()/emfBounds.getHeight());
|
||||
ctx.translate(-emfBounds.getCenterX(), -emfBounds.getCenterY());
|
||||
|
||||
|
||||
|
||||
int idx = 0;
|
||||
HemfGraphics g = new HemfGraphics(ctx, emfBounds);
|
||||
for (HemfRecord r : getRecords()) {
|
||||
g.draw(r);
|
||||
idx++;
|
||||
}
|
||||
} finally {
|
||||
ctx.setTransform(at);
|
||||
|
|
|
@ -323,11 +323,7 @@ public class HwmfGraphics {
|
|||
}
|
||||
|
||||
public void drawString(byte[] text, Rectangle2D bounds) {
|
||||
drawString(text, bounds, null);
|
||||
}
|
||||
|
||||
public void drawString(byte[] text, Rectangle2D bounds, List<Integer> dx) {
|
||||
drawString(text, bounds, dx, false);
|
||||
drawString(text, bounds, null, false);
|
||||
}
|
||||
|
||||
public void drawString(byte[] text, Rectangle2D bounds, List<Integer> dx, boolean isUnicode) {
|
||||
|
@ -352,10 +348,12 @@ public class HwmfGraphics {
|
|||
}
|
||||
|
||||
String textString = new String(text, charset).trim();
|
||||
if (textString.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
AttributedString as = new AttributedString(textString);
|
||||
if (dx == null || dx.isEmpty()) {
|
||||
addAttributes(as, font);
|
||||
} else {
|
||||
addAttributes(as, font);
|
||||
if (dx != null && !dx.isEmpty()) {
|
||||
//for multi-byte encodings (e.g. Shift_JIS), the byte length
|
||||
//might not equal the string length().
|
||||
//The x information is stored in dx[], an array parallel to the
|
||||
|
@ -371,31 +369,21 @@ public class HwmfGraphics {
|
|||
//dxNormed[0] = 13 textString.get(0) = U+30D7
|
||||
//dxNormed[1] = 14 textString.get(1) = U+30ED
|
||||
|
||||
final List<Integer> dxNormed;
|
||||
if (textString.length() == text.length) {
|
||||
dxNormed = new ArrayList<>(dx);
|
||||
} else {
|
||||
dxNormed = new ArrayList<>(dx.size());
|
||||
int dxPosition = 0;
|
||||
int[] chars = {0};
|
||||
for (int offset = 0; offset < textString.length(); ) {
|
||||
dxNormed.add(dx.get(dxPosition));
|
||||
chars[0] = textString.codePointAt(offset);
|
||||
//now figure out how many bytes it takes to encode that
|
||||
//code point in the charset
|
||||
int byteLength = new String(chars, 0, chars.length).getBytes(charset).length;
|
||||
dxPosition += byteLength;
|
||||
offset += Character.charCount(chars[0]);
|
||||
final int cps = textString.codePointCount(0, textString.length());
|
||||
final int unicodeSteps = Math.max(dx.size()/cps, 1);
|
||||
int dxPosition = 0;
|
||||
int beginIndex = 0;
|
||||
int[] chars = {0};
|
||||
while (beginIndex < textString.length() && dxPosition < dx.size()) {
|
||||
int endIndex = textString.offsetByCodePoints(beginIndex, 1);
|
||||
if (beginIndex > 0) {
|
||||
// Tracking works as a prefix/advance space on characters whereas
|
||||
// dx[...] is the complete width of the current char
|
||||
// therefore we need to add the additional/suffix width to the next char
|
||||
as.addAttribute(TextAttribute.TRACKING, (float)((dx.get(dxPosition) - fontW) / fontH), beginIndex, endIndex);
|
||||
}
|
||||
}
|
||||
|
||||
int cps = textString.codePointCount(0, textString.length());
|
||||
for (int i = 0; i < Math.min(dxNormed.size(),cps-1); i++) {
|
||||
addAttributes(as, font);
|
||||
// Tracking works as a prefix/advance space on characters whereas
|
||||
// dx[...] is the complete width of the current char
|
||||
// therefore we need to add the additional/suffix width to the next char
|
||||
as.addAttribute(TextAttribute.TRACKING, (dxNormed.get(i) - fontW) / fontH, i + 1, i + 2);
|
||||
dxPosition += (isUnicode) ? unicodeSteps : (endIndex-beginIndex);
|
||||
beginIndex = endIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,7 +401,7 @@ public class HwmfGraphics {
|
|||
graphicsCtx.fill(new Rectangle2D.Double(0, 0, bounds.getWidth(), bounds.getHeight()));
|
||||
}
|
||||
graphicsCtx.setColor(getProperties().getTextColor().getColor());
|
||||
graphicsCtx.drawString(as.getIterator(), 0, 0); // (float)bounds.getX(), (float)bounds.getY());
|
||||
graphicsCtx.drawString(as.getIterator(), 0, 0);
|
||||
} finally {
|
||||
graphicsCtx.setTransform(at);
|
||||
}
|
||||
|
@ -425,7 +413,9 @@ public class HwmfGraphics {
|
|||
|
||||
as.addAttribute(TextAttribute.FAMILY, fontInfo.getTypeface());
|
||||
as.addAttribute(TextAttribute.SIZE, getFontHeight(font));
|
||||
as.addAttribute(TextAttribute.STRIKETHROUGH, font.isStrikeOut());
|
||||
if (font.isStrikeOut()) {
|
||||
as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
|
||||
}
|
||||
if (font.isUnderline()) {
|
||||
as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
|
||||
}
|
||||
|
|
|
@ -415,6 +415,15 @@ public class HwmfFill {
|
|||
public void draw(HwmfGraphics ctx) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return
|
||||
"{ rasterOperation: '"+rasterOperation+"'"+
|
||||
", srcBounds: { x: "+srcBounds.getX()+", y: "+srcBounds.getY()+", w: "+srcBounds.getWidth()+", h: "+srcBounds.getHeight()+" }"+
|
||||
", dstBounds: { x: "+dstBounds.getX()+", y: "+dstBounds.getY()+", w: "+dstBounds.getWidth()+", h: "+dstBounds.getHeight()+" }"+
|
||||
"}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -377,7 +377,7 @@ public class HwmfMisc {
|
|||
*/
|
||||
public static class WmfDibCreatePatternBrush implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry {
|
||||
|
||||
private HwmfBrushStyle style;
|
||||
protected HwmfBrushStyle style;
|
||||
|
||||
/**
|
||||
* A 16-bit unsigned integer that defines whether the Colors field of a DIB
|
||||
|
@ -388,9 +388,9 @@ public class HwmfMisc {
|
|||
*
|
||||
* If the Style field specified anything but BS_PATTERN, this field MUST be one of the ColorUsage values.
|
||||
*/
|
||||
private ColorUsage colorUsage;
|
||||
protected ColorUsage colorUsage;
|
||||
|
||||
private HwmfBitmapDib patternDib;
|
||||
protected HwmfBitmapDib patternDib;
|
||||
private HwmfBitmap16 pattern16;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.nio.charset.Charset;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
|
||||
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
||||
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode;
|
||||
|
@ -39,6 +40,7 @@ import org.apache.poi.util.BitFieldFactory;
|
|||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.LittleEndianConsts;
|
||||
import org.apache.poi.util.LittleEndianInputStream;
|
||||
import org.apache.poi.util.LocaleUtil;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.apache.poi.util.RecordFormatException;
|
||||
|
@ -291,6 +293,10 @@ public class HwmfText {
|
|||
public boolean isClipped() {
|
||||
return ETO_CLIPPED.isSet(flag);
|
||||
}
|
||||
|
||||
public boolean isYDisplaced() {
|
||||
return ETO_PDY.isSet(flag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -393,37 +399,11 @@ public class HwmfText {
|
|||
@Override
|
||||
public void draw(HwmfGraphics ctx) {
|
||||
Rectangle2D bounds = new Rectangle2D.Double(reference.getX(), reference.getY(), 0, 0);
|
||||
ctx.drawString(rawTextBytes, bounds, dx);
|
||||
ctx.drawString(rawTextBytes, bounds, dx, false);
|
||||
}
|
||||
|
||||
|
||||
public String getText(Charset charset) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try (Reader r = new InputStreamReader(new ByteArrayInputStream(rawTextBytes), charset)) {
|
||||
for (int i = 0; i < stringLength; i++) {
|
||||
sb.appendCodePoint(readCodePoint(r));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
//TODO: move this to IOUtils?
|
||||
private int readCodePoint(Reader r) throws IOException {
|
||||
int c1 = r.read();
|
||||
if (c1 == -1) {
|
||||
throw new EOFException("Tried to read beyond byte array");
|
||||
}
|
||||
if (!Character.isHighSurrogate((char)c1)) {
|
||||
return c1;
|
||||
}
|
||||
int c2 = r.read();
|
||||
if (c2 == -1) {
|
||||
throw new EOFException("Tried to read beyond byte array");
|
||||
}
|
||||
if (!Character.isLowSurrogate((char)c2)) {
|
||||
throw new RecordFormatException("Expected low surrogate after high surrogate");
|
||||
}
|
||||
return Character.toCodePoint((char)c1, (char)c2);
|
||||
return new String(rawTextBytes, charset);
|
||||
}
|
||||
|
||||
public Point2D getReference() {
|
||||
|
@ -433,6 +413,25 @@ public class HwmfText {
|
|||
public Rectangle2D getBounds() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
protected boolean isUnicode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String text = "";
|
||||
try {
|
||||
text = getText(isUnicode() ? Charsets.UTF_16LE : LocaleUtil.CHARSET_1252);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
|
||||
return
|
||||
"{ reference: { x: "+reference.getX()+", y: "+reference.getY()+" }"+
|
||||
", bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+"}"+
|
||||
", text: '"+text.replaceAll("\\p{Cntrl}",".")+"'"+
|
||||
"}";
|
||||
}
|
||||
}
|
||||
|
||||
public enum HwmfTextAlignment {
|
||||
|
|
|
@ -38,6 +38,8 @@ import java.io.InputStream;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
|
@ -64,42 +66,61 @@ public class HemfPictureTest {
|
|||
@Test
|
||||
@Ignore("Only for manual tests")
|
||||
public void paint() throws IOException {
|
||||
File f = new File("picture_14.emf"); // sl_samples.getFile("wrench.emf");
|
||||
try (FileInputStream fis = new FileInputStream(f)) {
|
||||
HemfPicture emf = new HemfPicture(fis);
|
||||
// File f = new File("picture_14.emf"); // sl_samples.getFile("wrench.emf");
|
||||
// try (FileInputStream fis = new FileInputStream(f)) {
|
||||
|
||||
Dimension2D dim = emf.getSize();
|
||||
int width = Units.pointsToPixel(dim.getWidth());
|
||||
// keep aspect ratio for height
|
||||
int height = Units.pointsToPixel(dim.getHeight());
|
||||
double max = Math.max(width, height);
|
||||
if (max > 1500) {
|
||||
width *= 1500 / max;
|
||||
height *= 1500 / max;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
FileWriter fw = new FileWriter("record-list.txt");
|
||||
int i=0;
|
||||
for (HemfRecord r : emf.getRecords()) {
|
||||
if (r.getEmfRecordType() != HemfRecordType.comment) {
|
||||
fw.write(i + " " + r.getEmfRecordType() + " " + r.toString() + "\n");
|
||||
try (ZipInputStream zis = new ZipInputStream(new FileInputStream("tmp/emf.zip"))) {
|
||||
for (;;) {
|
||||
ZipEntry ze = zis.getNextEntry();
|
||||
if (ze == null) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
final File pngName = new File("build/tmp",ze.getName().replaceFirst( ".*/","").replace(".emf", ".png"));
|
||||
if (pngName.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 263/263282_000.emf
|
||||
// if (!ze.getName().contains("298/298837_000.emf")) continue;
|
||||
HemfPicture emf = new HemfPicture(zis);
|
||||
System.out.println(ze.getName());
|
||||
|
||||
Dimension2D dim = emf.getSize();
|
||||
int width = Units.pointsToPixel(dim.getWidth());
|
||||
// keep aspect ratio for height
|
||||
int height = Units.pointsToPixel(dim.getHeight());
|
||||
double max = Math.max(width, height);
|
||||
if (max > 1500) {
|
||||
width *= 1500 / max;
|
||||
height *= 1500 / max;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
FileWriter fw = new FileWriter("record-list.txt");
|
||||
int i = 0;
|
||||
for (HemfRecord r : emf.getRecords()) {
|
||||
if (r.getEmfRecordType() != HemfRecordType.comment) {
|
||||
fw.write(i + " " + r.getEmfRecordType() + " " + r.toString() + "\n");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
fw.close();
|
||||
|
||||
emf.draw(g, new Rectangle2D.Double(0, 0, width, height));
|
||||
|
||||
g.dispose();
|
||||
|
||||
ImageIO.write(bufImg, "PNG", pngName);
|
||||
|
||||
// break;
|
||||
}
|
||||
fw.close();
|
||||
|
||||
emf.draw(g, new Rectangle2D.Double(0,0,width,height));
|
||||
|
||||
g.dispose();
|
||||
|
||||
ImageIO.write(bufImg, "PNG", new File("bla.png"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue