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@1841897 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
5073f22fce
commit
b3fc9f4957
|
@ -33,7 +33,10 @@ import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
public class DrawPictureShape extends DrawSimpleShape {
|
public class DrawPictureShape extends DrawSimpleShape {
|
||||||
private static final POILogger LOG = POILogFactory.getLogger(DrawPictureShape.class);
|
private static final POILogger LOG = POILogFactory.getLogger(DrawPictureShape.class);
|
||||||
private static final ServiceLoader<ImageRenderer> rendererLoader = ServiceLoader.load(ImageRenderer.class);
|
private static final String[] KNOWN_RENDERER = {
|
||||||
|
"org.apache.poi.hwmf.draw.HwmfImageRenderer",
|
||||||
|
"org.apache.poi.hemf.draw.HemfImageRenderer"
|
||||||
|
};
|
||||||
|
|
||||||
public DrawPictureShape(PictureShape<?,?> shape) {
|
public DrawPictureShape(PictureShape<?,?> shape) {
|
||||||
super(shape);
|
super(shape);
|
||||||
|
@ -62,24 +65,44 @@ public class DrawPictureShape extends DrawSimpleShape {
|
||||||
* @param graphics the graphics context
|
* @param graphics the graphics context
|
||||||
* @return the image renderer
|
* @return the image renderer
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings({"WeakerAccess", "unchecked"})
|
||||||
public static ImageRenderer getImageRenderer(Graphics2D graphics, String contentType) {
|
public static ImageRenderer getImageRenderer(Graphics2D graphics, String contentType) {
|
||||||
ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
|
final ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
|
||||||
if (renderer != null) {
|
if (renderer != null && renderer.canRender(contentType)) {
|
||||||
return renderer;
|
return renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ImageRenderer ir : rendererLoader) {
|
// first try with our default image renderer
|
||||||
|
final BitmapImageRenderer bir = new BitmapImageRenderer();
|
||||||
|
if (bir.canRender(contentType)) {
|
||||||
|
return bir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// then iterate through the scratchpad renderers
|
||||||
|
//
|
||||||
|
// this could be nicely implemented via a j.u.ServiceLoader, but OSGi makes things complicated ...
|
||||||
|
// https://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html
|
||||||
|
// ... therefore falling back to classloading attempts
|
||||||
|
ClassLoader cl = ImageRenderer.class.getClassLoader();
|
||||||
|
for (String kr : KNOWN_RENDERER) {
|
||||||
|
final ImageRenderer ir;
|
||||||
|
try {
|
||||||
|
ir = ((Class<? extends ImageRenderer>)cl.loadClass(kr)).newInstance();
|
||||||
|
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||||
|
// scratchpad was not on the path, ignore and continue
|
||||||
|
LOG.log(POILogger.INFO, "Known image renderer '"+kr+" not found/loaded - include poi-scratchpad jar!", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (ir.canRender(contentType)) {
|
if (ir.canRender(contentType)) {
|
||||||
return ir;
|
return ir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.log(POILogger.ERROR, "No suiteable image renderer found for content-type '"+
|
LOG.log(POILogger.WARN, "No suiteable image renderer found for content-type '"+
|
||||||
contentType+"' - include poi-scratchpad jar!");
|
contentType+"' - include poi-scratchpad jar!");
|
||||||
|
|
||||||
// falling back to BitmapImageRenderer although this doesn't make much sense ...
|
// falling back to BitmapImageRenderer, at least it gracefully handles invalid images
|
||||||
return new BitmapImageRenderer();
|
return bir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
org.apache.poi.sl.draw.BitmapImageRenderer
|
|
|
@ -1,2 +0,0 @@
|
||||||
org.apache.poi.hwmf.draw.HwmfImageRenderer
|
|
||||||
org.apache.poi.hemf.draw.HemfImageRenderer
|
|
|
@ -24,16 +24,15 @@ import org.apache.poi.hwmf.draw.HwmfDrawProperties;
|
||||||
public class HemfDrawProperties extends HwmfDrawProperties {
|
public class HemfDrawProperties extends HwmfDrawProperties {
|
||||||
|
|
||||||
/** Path for path bracket operations */
|
/** Path for path bracket operations */
|
||||||
protected final Path2D path;
|
protected Path2D path = null;
|
||||||
|
|
||||||
|
|
||||||
public HemfDrawProperties() {
|
public HemfDrawProperties() {
|
||||||
path = new Path2D.Double();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HemfDrawProperties(HemfDrawProperties other) {
|
public HemfDrawProperties(HemfDrawProperties other) {
|
||||||
super(other);
|
super(other);
|
||||||
path = (Path2D)other.path.clone();
|
path = (other.path != null) ? (Path2D)other.path.clone() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,4 +41,21 @@ public class HemfDrawProperties extends HwmfDrawProperties {
|
||||||
public Path2D getPath() {
|
public Path2D getPath() {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Un-/Sets the bracket path
|
||||||
|
* @param path the bracket path
|
||||||
|
*/
|
||||||
|
public void setPath(Path2D path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use path (bracket) or graphics context for drawing operations
|
||||||
|
* @return {@code true}, if the drawing should go to the path bracket,
|
||||||
|
* if {@code false} draw directly to the graphics context
|
||||||
|
*/
|
||||||
|
public boolean usePathBracket() {
|
||||||
|
return path != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,18 @@ package org.apache.poi.hemf.draw;
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.apache.poi.hemf.record.emf.HemfBounded;
|
import org.apache.poi.hemf.record.emf.HemfBounded;
|
||||||
import org.apache.poi.hemf.record.emf.HemfRecord;
|
import org.apache.poi.hemf.record.emf.HemfRecord;
|
||||||
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
||||||
|
import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
|
||||||
|
import org.apache.poi.util.Internal;
|
||||||
|
|
||||||
public class HemfGraphics extends HwmfGraphics {
|
public class HemfGraphics extends HwmfGraphics {
|
||||||
|
|
||||||
|
@ -80,6 +85,59 @@ public class HemfGraphics extends HwmfGraphics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public void draw(Consumer<Path2D> pathConsumer) {
|
||||||
|
final HemfDrawProperties prop = getProperties();
|
||||||
|
final boolean useBracket = prop.usePathBracket();
|
||||||
|
|
||||||
|
final Path2D path;
|
||||||
|
if (useBracket) {
|
||||||
|
path = prop.getPath();
|
||||||
|
} else {
|
||||||
|
path = new Path2D.Double();
|
||||||
|
Point2D pnt = prop.getLocation();
|
||||||
|
path.moveTo(pnt.getX(),pnt.getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
pathConsumer.accept(path);
|
||||||
|
|
||||||
|
prop.setLocation(path.getCurrentPoint());
|
||||||
|
if (!useBracket) {
|
||||||
|
// TODO: when to use draw vs. fill?
|
||||||
|
graphicsCtx.draw(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds or sets an record of type {@link HwmfObjectTableEntry} to the object table.
|
||||||
|
* If the {@code index} is less than 1, the method acts the same as
|
||||||
|
* {@link HwmfGraphics#addObjectTableEntry(HwmfObjectTableEntry)}, otherwise the
|
||||||
|
* index is used to access the object table.
|
||||||
|
* As the table is filled successively, the index must be between 1 and size+1
|
||||||
|
*
|
||||||
|
* @param entry the record to be stored
|
||||||
|
* @param index the index to be overwritten, regardless if its content was unset before
|
||||||
|
*
|
||||||
|
* @see HwmfGraphics#addObjectTableEntry(HwmfObjectTableEntry)
|
||||||
|
*/
|
||||||
|
public void addObjectTableEntry(HwmfObjectTableEntry entry, int index) {
|
||||||
|
if (index < 1) {
|
||||||
|
super.addObjectTableEntry(entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > objectTable.size()) {
|
||||||
|
throw new IllegalStateException("object table hasn't grown to this index yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == objectTable.size()) {
|
||||||
|
objectTable.add(entry);
|
||||||
|
} else {
|
||||||
|
objectTable.set(index, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** saves the current affine transform on the stack */
|
/** saves the current affine transform on the stack */
|
||||||
private void saveTransform() {
|
private void saveTransform() {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.apache.poi.util.IOUtils;
|
||||||
import org.apache.poi.util.Internal;
|
import org.apache.poi.util.Internal;
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
import org.apache.poi.util.LittleEndianInputStream;
|
import org.apache.poi.util.LittleEndianInputStream;
|
||||||
|
import org.apache.poi.util.LocaleUtil;
|
||||||
import org.apache.poi.util.RecordFormatException;
|
import org.apache.poi.util.RecordFormatException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,6 +98,11 @@ public class HemfComment {
|
||||||
public EmfCommentData getCommentData() {
|
public EmfCommentData getCommentData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ data: "+data+" }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EmfCommentDataIterator implements Iterator<EmfCommentData> {
|
public static class EmfCommentDataIterator implements Iterator<EmfCommentData> {
|
||||||
|
@ -208,6 +214,11 @@ public class HemfComment {
|
||||||
leis.readFully(privateData);
|
leis.readFully(privateData);
|
||||||
return privateData.length;
|
return privateData.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "\""+new String(privateData, LocaleUtil.CHARSET_1252)+"\"";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The EMR_COMMENT_EMFPLUS record contains embedded EMF+ records. */
|
/** The EMR_COMMENT_EMFPLUS record contains embedded EMF+ records. */
|
||||||
|
|
|
@ -53,6 +53,28 @@ public class HemfDraw {
|
||||||
private static final HwmfColorRef DKGRAY = new HwmfColorRef(new Color(0x00404040));
|
private static final HwmfColorRef DKGRAY = new HwmfColorRef(new Color(0x00404040));
|
||||||
private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK);
|
private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK);
|
||||||
|
|
||||||
|
private static final String[] STOCK_IDS = {
|
||||||
|
"0x80000000 /* WHITE_BRUSH */",
|
||||||
|
"0x80000001 /* LTGRAY_BRUSH */",
|
||||||
|
"0x80000002 /* GRAY_BRUSH */",
|
||||||
|
"0x80000003 /* DKGRAY_BRUSH */",
|
||||||
|
"0x80000004 /* BLACK_BRUSH */",
|
||||||
|
"0x80000005 /* NULL_BRUSH */",
|
||||||
|
"0x80000006 /* WHITE_PEN */",
|
||||||
|
"0x80000007 /* BLACK_PEN */",
|
||||||
|
"0x80000008 /* NULL_PEN */",
|
||||||
|
"0x8000000A /* OEM_FIXED_FONT */",
|
||||||
|
"0x8000000B /* ANSI_FIXED_FONT */",
|
||||||
|
"0x8000000C /* ANSI_VAR_FONT */",
|
||||||
|
"0x8000000D /* SYSTEM_FONT */",
|
||||||
|
"0x8000000E /* DEVICE_DEFAULT_FONT */",
|
||||||
|
"0x8000000F /* DEFAULT_PALETTE */",
|
||||||
|
"0x80000010 /* SYSTEM_FIXED_FONT */",
|
||||||
|
"0x80000011 /* DEFAULT_GUI_FONT */",
|
||||||
|
"0x80000012 /* DC_BRUSH */",
|
||||||
|
"0x80000013 /* DC_PEN */"
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HemfRecordType getEmfRecordType() {
|
public HemfRecordType getEmfRecordType() {
|
||||||
return HemfRecordType.selectObject;
|
return HemfRecordType.selectObject;
|
||||||
|
@ -184,6 +206,14 @@ public class HemfDraw {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ index: "+
|
||||||
|
(((objectIndex & 0x80000000) != 0 && (objectIndex & 0x3FFFFFFF) <= 13 )
|
||||||
|
? STOCK_IDS[objectIndex & 0x3FFFFFFF]
|
||||||
|
: objectIndex)+" }";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -220,7 +250,7 @@ public class HemfDraw {
|
||||||
final int points = Math.min(count, 16384);
|
final int points = Math.min(count, 16384);
|
||||||
size += LittleEndianConsts.INT_SIZE;
|
size += LittleEndianConsts.INT_SIZE;
|
||||||
|
|
||||||
poly.reset();
|
poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, points);
|
||||||
|
|
||||||
/* Cubic Bezier curves are defined using the endpoints and control points
|
/* Cubic Bezier curves are defined using the endpoints and control points
|
||||||
* specified by the points field. The first curve is drawn from the first
|
* specified by the points field. The first curve is drawn from the first
|
||||||
|
@ -324,6 +354,8 @@ public class HemfDraw {
|
||||||
final int points = Math.min(count, 16384);
|
final int points = Math.min(count, 16384);
|
||||||
size += LittleEndianConsts.INT_SIZE;
|
size += LittleEndianConsts.INT_SIZE;
|
||||||
|
|
||||||
|
poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, points);
|
||||||
|
|
||||||
Point2D pnt = new Point2D.Double();
|
Point2D pnt = new Point2D.Double();
|
||||||
for (int i=0; i<points; i++) {
|
for (int i=0; i<points; i++) {
|
||||||
size += readPoint(leis, pnt);
|
size += readPoint(leis, pnt);
|
||||||
|
@ -541,7 +573,7 @@ public class HemfDraw {
|
||||||
* An array of WMF PointL objects that specifies the points for all polygons in logical units.
|
* An array of WMF PointL objects that specifies the points for all polygons in logical units.
|
||||||
* The number of points is specified by the Count field value.
|
* The number of points is specified by the Count field value.
|
||||||
*/
|
*/
|
||||||
Path2D poly = new Path2D.Double();
|
Path2D poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, (int)nPoints);
|
||||||
for (int i=0; i<nPoints; i++) {
|
for (int i=0; i<nPoints; i++) {
|
||||||
size += readPoint(leis, pnt);
|
size += readPoint(leis, pnt);
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
|
@ -659,11 +691,8 @@ public class HemfDraw {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HemfGraphics ctx) {
|
public void draw(final HemfGraphics ctx) {
|
||||||
HemfDrawProperties prop = ctx.getProperties();
|
ctx.draw((path) -> path.moveTo(point.getX(), point.getY()));
|
||||||
final Path2D path = prop.getPath();
|
|
||||||
path.moveTo(point.getX(), point.getY());
|
|
||||||
prop.setLocation(point);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -802,11 +831,8 @@ public class HemfDraw {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HemfGraphics ctx) {
|
public void draw(final HemfGraphics ctx) {
|
||||||
final HemfDrawProperties prop = ctx.getProperties();
|
ctx.draw((path) -> path.lineTo(point.getX(), point.getY()));
|
||||||
final Path2D path = prop.getPath();
|
|
||||||
path.lineTo(point.getX(), point.getY());
|
|
||||||
prop.setLocation(point);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,11 +855,9 @@ public class HemfDraw {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HemfGraphics ctx) {
|
public void draw(final HemfGraphics ctx) {
|
||||||
final Path2D path = ctx.getProperties().getPath();
|
final Arc2D arc = getShape();
|
||||||
Arc2D arc = getShape();
|
ctx.draw((path) -> path.append(arc, true));
|
||||||
path.append(arc, true);
|
|
||||||
ctx.getProperties().setLocation(endPoint);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -860,7 +884,7 @@ public class HemfDraw {
|
||||||
size += readPoint(leis, points[i]);
|
size += readPoint(leis, points[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
poly.reset();
|
poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, count);
|
||||||
|
|
||||||
for (int i=0; i<count; i++) {
|
for (int i=0; i<count; i++) {
|
||||||
int mode = leis.readUByte();
|
int mode = leis.readUByte();
|
||||||
|
@ -958,8 +982,7 @@ public class HemfDraw {
|
||||||
@Override
|
@Override
|
||||||
public void draw(HemfGraphics ctx) {
|
public void draw(HemfGraphics ctx) {
|
||||||
final HemfDrawProperties prop = ctx.getProperties();
|
final HemfDrawProperties prop = ctx.getProperties();
|
||||||
final Path2D path = prop.getPath();
|
prop.setPath(new Path2D.Double());
|
||||||
path.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -996,8 +1019,7 @@ public class HemfDraw {
|
||||||
@Override
|
@Override
|
||||||
public void draw(HemfGraphics ctx) {
|
public void draw(HemfGraphics ctx) {
|
||||||
final HemfDrawProperties prop = ctx.getProperties();
|
final HemfDrawProperties prop = ctx.getProperties();
|
||||||
final Path2D path = prop.getPath();
|
prop.setPath(null);
|
||||||
path.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1036,8 +1058,10 @@ public class HemfDraw {
|
||||||
public void draw(HemfGraphics ctx) {
|
public void draw(HemfGraphics ctx) {
|
||||||
final HemfDrawProperties prop = ctx.getProperties();
|
final HemfDrawProperties prop = ctx.getProperties();
|
||||||
final Path2D path = prop.getPath();
|
final Path2D path = prop.getPath();
|
||||||
|
if (path != null) {
|
||||||
path.closePath();
|
path.closePath();
|
||||||
prop.setLocation(path.getCurrentPoint());
|
prop.setLocation(path.getCurrentPoint());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1140,16 +1164,12 @@ public class HemfDraw {
|
||||||
return 2*LittleEndianConsts.INT_SIZE;
|
return 2*LittleEndianConsts.INT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void polyTo(HemfGraphics ctx, Path2D poly) {
|
private static void polyTo(final HemfGraphics ctx, final Path2D poly) {
|
||||||
final HemfDrawProperties prop = ctx.getProperties();
|
final PathIterator pi = poly.getPathIterator(null);
|
||||||
final Path2D path = prop.getPath();
|
|
||||||
PathIterator pi = poly.getPathIterator(null);
|
|
||||||
// ignore dummy start point (moveTo)
|
// ignore dummy start point (moveTo)
|
||||||
pi.next();
|
pi.next();
|
||||||
assert (!pi.isDone());
|
assert (!pi.isDone());
|
||||||
path.append(pi, true);
|
|
||||||
prop.setLocation(path.getCurrentPoint());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
ctx.draw((path) -> path.append(pi, true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -655,6 +655,9 @@ public class HemfFill {
|
||||||
@Override
|
@Override
|
||||||
public void draw(HemfGraphics ctx) {
|
public void draw(HemfGraphics ctx) {
|
||||||
final HemfDrawProperties prop = ctx.getProperties();
|
final HemfDrawProperties prop = ctx.getProperties();
|
||||||
|
if (!prop.usePathBracket()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final Path2D path = (Path2D)prop.getPath().clone();
|
final Path2D path = (Path2D)prop.getPath().clone();
|
||||||
path.setWindingRule(ctx.getProperties().getWindingRule());
|
path.setWindingRule(ctx.getProperties().getWindingRule());
|
||||||
if (prop.getBrushStyle() == HwmfBrushStyle.BS_NULL) {
|
if (prop.getBrushStyle() == HwmfBrushStyle.BS_NULL) {
|
||||||
|
|
|
@ -98,7 +98,7 @@ public class HemfMisc {
|
||||||
/**
|
/**
|
||||||
* The EMF_SAVEDC record saves the playback device context for later retrieval.
|
* The EMF_SAVEDC record saves the playback device context for later retrieval.
|
||||||
*/
|
*/
|
||||||
public static class EmfSaveDc implements HemfRecord {
|
public static class EmfSaveDc extends HwmfMisc.WmfSaveDc implements HemfRecord {
|
||||||
@Override
|
@Override
|
||||||
public HemfRecordType getEmfRecordType() {
|
public HemfRecordType getEmfRecordType() {
|
||||||
return HemfRecordType.saveDc;
|
return HemfRecordType.saveDc;
|
||||||
|
@ -108,25 +108,13 @@ 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 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(HemfGraphics ctx) {
|
|
||||||
ctx.saveProperties();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The EMF_RESTOREDC record restores the playback device context from a previously saved device
|
* The EMF_RESTOREDC record restores the playback device context from a previously saved device
|
||||||
* context.
|
* context.
|
||||||
*/
|
*/
|
||||||
public static class EmfRestoreDc implements HemfRecord {
|
public static class EmfRestoreDc extends HwmfMisc.WmfRestoreDc implements HemfRecord {
|
||||||
|
|
||||||
/**
|
|
||||||
* SavedDC (4 bytes): A 32-bit signed integer that specifies the saved state to restore relative to
|
|
||||||
* the current state. This value MUST be negative; –1 represents the state that was most
|
|
||||||
* recently saved on the stack, –2 the one before that, etc.
|
|
||||||
*/
|
|
||||||
private int nSavedDC;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HemfRecordType getEmfRecordType() {
|
public HemfRecordType getEmfRecordType() {
|
||||||
|
@ -135,23 +123,19 @@ public class HemfMisc {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
||||||
|
// A 32-bit signed integer that specifies the saved state to restore relative to
|
||||||
|
// the current state. This value MUST be negative; –1 represents the state that was most
|
||||||
|
// recently saved on the stack, –2 the one before that, etc.
|
||||||
nSavedDC = leis.readInt();
|
nSavedDC = leis.readInt();
|
||||||
return LittleEndianConsts.INT_SIZE;
|
return LittleEndianConsts.INT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(HemfGraphics ctx) {
|
|
||||||
ctx.restoreProperties(nSavedDC);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The META_SETBKCOLOR record sets the background color in the playback device context to a
|
* The META_SETBKCOLOR record sets the background color in the playback device context to a
|
||||||
* specified color, or to the nearest physical color if the device cannot represent the specified color.
|
* specified color, or to the nearest physical color if the device cannot represent the specified color.
|
||||||
*/
|
*/
|
||||||
public static class EmfSetBkColor implements HemfRecord {
|
public static class EmfSetBkColor extends HwmfMisc.WmfSetBkColor implements HemfRecord {
|
||||||
|
|
||||||
private HwmfColorRef colorRef;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HemfRecordType getEmfRecordType() {
|
public HemfRecordType getEmfRecordType() {
|
||||||
|
@ -160,14 +144,8 @@ public class HemfMisc {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
|
||||||
colorRef = new HwmfColorRef();
|
|
||||||
return colorRef.init(leis);
|
return colorRef.init(leis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(HemfGraphics ctx) {
|
|
||||||
ctx.getProperties().setBackgroundColor(colorRef);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -287,8 +265,13 @@ public class HemfMisc {
|
||||||
int size = colorRef.init(leis);
|
int size = colorRef.init(leis);
|
||||||
brushHatch = HwmfHatchStyle.valueOf((int) leis.readUInt());
|
brushHatch = HwmfHatchStyle.valueOf((int) leis.readUInt());
|
||||||
return size + 3 * LittleEndianConsts.INT_SIZE;
|
return size + 3 * LittleEndianConsts.INT_SIZE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(HemfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this, brushIdx);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -341,6 +324,11 @@ public class HemfMisc {
|
||||||
|
|
||||||
return size + 4 * LittleEndianConsts.INT_SIZE;
|
return size + 4 * LittleEndianConsts.INT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(HemfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this, penIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EmfExtCreatePen extends EmfCreatePen {
|
public static class EmfExtCreatePen extends EmfCreatePen {
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.poi.hemf.record.emf;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.poi.hemf.draw.HemfGraphics;
|
||||||
import org.apache.poi.hwmf.record.HwmfPalette;
|
import org.apache.poi.hwmf.record.HwmfPalette;
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
import org.apache.poi.util.LittleEndianInputStream;
|
import org.apache.poi.util.LittleEndianInputStream;
|
||||||
|
@ -67,6 +68,11 @@ public class HemfPalette {
|
||||||
int size = readPaletteEntries(leis, -1);
|
int size = readPaletteEntries(leis, -1);
|
||||||
return size + LittleEndianConsts.INT_SIZE + LittleEndianConsts.SHORT_SIZE;
|
return size + LittleEndianConsts.INT_SIZE + LittleEndianConsts.SHORT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(HemfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this, paletteIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,6 +99,11 @@ public class HemfPalette {
|
||||||
int size = readPaletteEntries(leis, nbrOfEntries);
|
int size = readPaletteEntries(leis, nbrOfEntries);
|
||||||
return size + 3*LittleEndianConsts.INT_SIZE;
|
return size + 3*LittleEndianConsts.INT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(HemfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this, paletteIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,6 +129,11 @@ public class HemfPalette {
|
||||||
|
|
||||||
return 2*LittleEndianConsts.INT_SIZE;
|
return 2*LittleEndianConsts.INT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(HemfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this, paletteIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -47,7 +47,7 @@ public enum HemfRecordType {
|
||||||
setStretchBltMode(0x00000015, HemfMisc.EmfSetStretchBltMode::new),
|
setStretchBltMode(0x00000015, HemfMisc.EmfSetStretchBltMode::new),
|
||||||
setTextAlign(0x00000016, HemfText.EmfSetTextAlign::new),
|
setTextAlign(0x00000016, HemfText.EmfSetTextAlign::new),
|
||||||
setcoloradjustment(0x00000017, UnimplementedHemfRecord::new),
|
setcoloradjustment(0x00000017, UnimplementedHemfRecord::new),
|
||||||
setTextColor(0x00000018, HemfText.SetTextColor::new),
|
setTextColor(0x00000018, HemfText.EmfSetTextColor::new),
|
||||||
setBkColor(0x00000019, HemfMisc.EmfSetBkColor::new),
|
setBkColor(0x00000019, HemfMisc.EmfSetBkColor::new),
|
||||||
setOffsetClipRgn(0x0000001A, HemfWindowing.EmfSetOffsetClipRgn::new),
|
setOffsetClipRgn(0x0000001A, HemfWindowing.EmfSetOffsetClipRgn::new),
|
||||||
setMoveToEx(0x0000001B, HemfDraw.EmfSetMoveToEx::new),
|
setMoveToEx(0x0000001B, HemfDraw.EmfSetMoveToEx::new),
|
||||||
|
@ -106,8 +106,8 @@ public enum HemfRecordType {
|
||||||
setDiBitsToDevice(0x00000050, HemfFill.EmfSetDiBitsToDevice::new),
|
setDiBitsToDevice(0x00000050, HemfFill.EmfSetDiBitsToDevice::new),
|
||||||
stretchdibits(0x00000051, UnimplementedHemfRecord::new),
|
stretchdibits(0x00000051, UnimplementedHemfRecord::new),
|
||||||
extCreateFontIndirectW(0x00000052, HemfText.ExtCreateFontIndirectW::new),
|
extCreateFontIndirectW(0x00000052, HemfText.ExtCreateFontIndirectW::new),
|
||||||
exttextouta(0x00000053, HemfText.ExtTextOutA::new),
|
exttextouta(0x00000053, HemfText.EmfExtTextOutA::new),
|
||||||
exttextoutw(0x00000054, HemfText.ExtTextOutW::new),
|
exttextoutw(0x00000054, HemfText.EmfExtTextOutW::new),
|
||||||
polyBezier16(0x00000055, HemfDraw.EmfPolyBezier16::new),
|
polyBezier16(0x00000055, HemfDraw.EmfPolyBezier16::new),
|
||||||
polygon16(0x00000056, HemfDraw.EmfPolygon16::new),
|
polygon16(0x00000056, HemfDraw.EmfPolygon16::new),
|
||||||
polyline16(0x00000057, HemfDraw.EmfPolyline16::new),
|
polyline16(0x00000057, HemfDraw.EmfPolyline16::new),
|
||||||
|
|
|
@ -29,7 +29,7 @@ import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.poi.hwmf.record.HwmfColorRef;
|
import org.apache.poi.hemf.draw.HemfGraphics;
|
||||||
import org.apache.poi.hwmf.record.HwmfText;
|
import org.apache.poi.hwmf.record.HwmfText;
|
||||||
import org.apache.poi.hwmf.record.HwmfText.WmfSetTextAlign;
|
import org.apache.poi.hwmf.record.HwmfText.WmfSetTextAlign;
|
||||||
import org.apache.poi.util.Dimension2DDouble;
|
import org.apache.poi.util.Dimension2DDouble;
|
||||||
|
@ -53,9 +53,9 @@ public class HemfText {
|
||||||
GM_COMPATIBLE, GM_ADVANCED
|
GM_COMPATIBLE, GM_ADVANCED
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ExtTextOutA implements HemfRecord {
|
public static class EmfExtTextOutA implements HemfRecord {
|
||||||
|
|
||||||
protected final Rectangle2D boundsIgnored = new Rectangle2D.Double();
|
protected final Rectangle2D bounds = new Rectangle2D.Double();
|
||||||
|
|
||||||
protected EmfGraphicsMode graphicsMode;
|
protected EmfGraphicsMode graphicsMode;
|
||||||
|
|
||||||
|
@ -67,11 +67,11 @@ public class HemfText {
|
||||||
|
|
||||||
protected final EmrTextObject textObject;
|
protected final EmrTextObject textObject;
|
||||||
|
|
||||||
public ExtTextOutA() {
|
public EmfExtTextOutA() {
|
||||||
this(false);
|
this(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ExtTextOutA(boolean isUnicode) {
|
protected EmfExtTextOutA(boolean isUnicode) {
|
||||||
textObject = new EmrTextObject(isUnicode);
|
textObject = new EmrTextObject(isUnicode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,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, boundsIgnored);
|
long size = readRectL(leis, bounds);
|
||||||
|
|
||||||
// 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];
|
||||||
|
@ -104,10 +104,10 @@ public class HemfText {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* To be implemented! We need to get the current character set
|
* To be implemented! We need to get the current character set
|
||||||
* from the current font for {@link ExtTextOutA},
|
* from the current font for {@link EmfExtTextOutA},
|
||||||
* which has to be tracked in the playback device.
|
* which has to be tracked in the playback device.
|
||||||
*
|
*
|
||||||
* For {@link ExtTextOutW}, the charset is "UTF-16LE"
|
* For {@link EmfExtTextOutW}, the charset is "UTF-16LE"
|
||||||
*
|
*
|
||||||
* @param charset the charset to be used to decode the character bytes
|
* @param charset the charset to be used to decode the character bytes
|
||||||
* @return text from this text element
|
* @return text from this text element
|
||||||
|
@ -132,11 +132,24 @@ public class HemfText {
|
||||||
public Dimension2D getScale() {
|
public Dimension2D getScale() {
|
||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return
|
||||||
|
"{ bounds: { x: "+bounds.getX()+
|
||||||
|
", y: "+bounds.getY()+
|
||||||
|
", w: "+bounds.getWidth()+
|
||||||
|
", h: "+bounds.getHeight()+
|
||||||
|
"}, graphicsMode: '"+graphicsMode+"'"+
|
||||||
|
", scale: { w: "+scale.getWidth()+", h: "+scale.getHeight()+" }"+
|
||||||
|
", textObject: "+textObject+
|
||||||
|
"}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ExtTextOutW extends ExtTextOutA {
|
public static class EmfExtTextOutW extends EmfExtTextOutA {
|
||||||
|
|
||||||
public ExtTextOutW() {
|
public EmfExtTextOutW() {
|
||||||
super(true);
|
super(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,10 +188,7 @@ public class HemfText {
|
||||||
/**
|
/**
|
||||||
* The EMR_SETTEXTCOLOR record defines the current text color.
|
* The EMR_SETTEXTCOLOR record defines the current text color.
|
||||||
*/
|
*/
|
||||||
public static class SetTextColor implements HemfRecord {
|
public static class EmfSetTextColor extends HwmfText.WmfSetTextColor implements HemfRecord {
|
||||||
/** A WMF ColorRef object that specifies the text color value. */
|
|
||||||
private final HwmfColorRef colorRef = new HwmfColorRef();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HemfRecordType getEmfRecordType() {
|
public HemfRecordType getEmfRecordType() {
|
||||||
return HemfRecordType.setTextColor;
|
return HemfRecordType.setTextColor;
|
||||||
|
@ -284,6 +294,16 @@ public class HemfText {
|
||||||
int size = font.init(leis, (int)(recordSize-LittleEndianConsts.INT_SIZE));
|
int size = font.init(leis, (int)(recordSize-LittleEndianConsts.INT_SIZE));
|
||||||
return size+LittleEndianConsts.INT_SIZE;
|
return size+LittleEndianConsts.INT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(HemfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this, fontIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ index: "+fontIdx+", font: "+font+" } ";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EmfExtTextOutOptions extends HwmfText.WmfExtTextOutOptions {
|
public static class EmfExtTextOutOptions extends HwmfText.WmfExtTextOutOptions {
|
||||||
|
|
|
@ -55,9 +55,9 @@ public class HwmfGraphics {
|
||||||
protected final List<HwmfDrawProperties> propStack = new LinkedList<>();
|
protected final List<HwmfDrawProperties> propStack = new LinkedList<>();
|
||||||
protected HwmfDrawProperties prop;
|
protected HwmfDrawProperties prop;
|
||||||
protected final Graphics2D graphicsCtx;
|
protected final Graphics2D graphicsCtx;
|
||||||
|
protected final List<HwmfObjectTableEntry> objectTable = new ArrayList<>();
|
||||||
|
|
||||||
private static final Charset DEFAULT_CHARSET = LocaleUtil.CHARSET_1252;
|
private static final Charset DEFAULT_CHARSET = LocaleUtil.CHARSET_1252;
|
||||||
private final List<HwmfObjectTableEntry> objectTable = new ArrayList<>();
|
|
||||||
/** Bounding box from the placeable header */
|
/** Bounding box from the placeable header */
|
||||||
private final Rectangle2D bbox;
|
private final Rectangle2D bbox;
|
||||||
private final AffineTransform initialAT;
|
private final AffineTransform initialAT;
|
||||||
|
|
|
@ -69,4 +69,9 @@ public class HwmfColorRef implements Cloneable {
|
||||||
throw new InternalError();
|
throw new InternalError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%#8X", colorRef.getRGB());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.awt.geom.Area;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.Line2D;
|
import java.awt.geom.Line2D;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
|
import java.awt.geom.PathIterator;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
@ -57,6 +58,11 @@ public class HwmfDraw {
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
ctx.getProperties().setLocation(point);
|
ctx.getProperties().setLocation(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ x: "+point.getX()+", y: "+point.getY()+" }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,6 +90,11 @@ public class HwmfDraw {
|
||||||
ctx.draw(line);
|
ctx.draw(line);
|
||||||
ctx.getProperties().setLocation(point);
|
ctx.getProperties().setLocation(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ x: "+point.getX()+", y: "+point.getY()+" }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,7 +104,7 @@ public class HwmfDraw {
|
||||||
*/
|
*/
|
||||||
public static class WmfPolygon implements HwmfRecord {
|
public static class WmfPolygon implements HwmfRecord {
|
||||||
|
|
||||||
protected Path2D poly = new Path2D.Double();
|
protected Path2D poly;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HwmfRecordType getWmfRecordType() {
|
public HwmfRecordType getWmfRecordType() {
|
||||||
|
@ -107,6 +118,7 @@ public class HwmfDraw {
|
||||||
*/
|
*/
|
||||||
int numberofPoints = leis.readShort();
|
int numberofPoints = leis.readShort();
|
||||||
|
|
||||||
|
poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, numberofPoints);
|
||||||
for (int i=0; i<numberofPoints; i++) {
|
for (int i=0; i<numberofPoints; i++) {
|
||||||
// A 16-bit signed integer that defines the horizontal (x) coordinate of the point.
|
// A 16-bit signed integer that defines the horizontal (x) coordinate of the point.
|
||||||
int x = leis.readShort();
|
int x = leis.readShort();
|
||||||
|
@ -134,6 +146,11 @@ public class HwmfDraw {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ poly: "+polyToString(poly)+" }";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true, if the shape should be filled
|
* @return true, if the shape should be filled
|
||||||
*/
|
*/
|
||||||
|
@ -279,7 +296,7 @@ public class HwmfDraw {
|
||||||
* An array of 16-bit signed integers that define the coordinates of the polygons.
|
* An array of 16-bit signed integers that define the coordinates of the polygons.
|
||||||
* (Note: MS-WMF wrongly says unsigned integers ...)
|
* (Note: MS-WMF wrongly says unsigned integers ...)
|
||||||
*/
|
*/
|
||||||
Path2D poly = new Path2D.Double();
|
Path2D poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, nPoints);
|
||||||
for (int i=0; i<nPoints; i++) {
|
for (int i=0; i<nPoints; i++) {
|
||||||
int x = leis.readShort();
|
int x = leis.readShort();
|
||||||
int y = leis.readShort();
|
int y = leis.readShort();
|
||||||
|
@ -556,6 +573,11 @@ public class HwmfDraw {
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
ctx.applyObjectTableEntry(objectIndex);
|
ctx.applyObjectTableEntry(objectIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ index: "+objectIndex +" }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int readBounds(LittleEndianInputStream leis, Rectangle2D bounds) {
|
static int readBounds(LittleEndianInputStream leis, Rectangle2D bounds) {
|
||||||
|
@ -603,4 +625,34 @@ public class HwmfDraw {
|
||||||
point.setLocation(x, y);
|
point.setLocation(x, y);
|
||||||
return 2*LittleEndianConsts.SHORT_SIZE;
|
return 2*LittleEndianConsts.SHORT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String polyToString(Path2D poly) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("[");
|
||||||
|
final PathIterator iter = poly.getPathIterator(null);
|
||||||
|
double[] pnts = new double[6];
|
||||||
|
while (!iter.isDone()) {
|
||||||
|
int segType = iter.currentSegment(pnts);
|
||||||
|
switch (segType) {
|
||||||
|
case PathIterator.SEG_MOVETO:
|
||||||
|
sb.append("{ type: 'move', x: "+pnts[0]+", y: "+pnts[1]+" }, ");
|
||||||
|
break;
|
||||||
|
case PathIterator.SEG_LINETO:
|
||||||
|
sb.append("{ type: 'lineto', x: "+pnts[0]+", y: "+pnts[1]+" }, ");
|
||||||
|
break;
|
||||||
|
case PathIterator.SEG_QUADTO:
|
||||||
|
sb.append("{ type: 'quad', x1: "+pnts[0]+", y1: "+pnts[1]+", x2: "+pnts[2]+", y2: "+pnts[3]+" }, ");
|
||||||
|
break;
|
||||||
|
case PathIterator.SEG_CUBICTO:
|
||||||
|
sb.append("{ type: 'cubic', x1: "+pnts[0]+", y1: "+pnts[1]+", x2: "+pnts[2]+", y2: "+pnts[3]+", x3: "+pnts[4]+", y3: "+pnts[5]+" }, ");
|
||||||
|
break;
|
||||||
|
case PathIterator.SEG_CLOSE:
|
||||||
|
sb.append("{ type: 'close' }, ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
iter.next();
|
||||||
|
}
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,6 +146,20 @@ public class HwmfFont implements FontInfo {
|
||||||
flag = leis.readUByte();
|
flag = leis.readUByte();
|
||||||
return LittleEndianConsts.BYTE_SIZE;
|
return LittleEndianConsts.BYTE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return
|
||||||
|
(((flag&0x3) == 0 ? "default " : " ")+
|
||||||
|
(CLIP_CHARACTER_PRECIS.isSet(flag) ? "char " : " ")+
|
||||||
|
(CLIP_STROKE_PRECIS.isSet(flag) ? "stroke " : " ")+
|
||||||
|
(CLIP_LH_ANGLES.isSet(flag) ? "angles " : " ")+
|
||||||
|
(CLIP_TT_ALWAYS.isSet(flag) ? "tt_always " : " ")+
|
||||||
|
(CLIP_DFA_DISABLE.isSet(flag) ? "dfa " : " ")+
|
||||||
|
(CLIP_EMBEDDED.isSet(flag) ? "embedded " : " ")
|
||||||
|
).trim()
|
||||||
|
;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -453,6 +467,25 @@ public class HwmfFont implements FontInfo {
|
||||||
throw new UnsupportedOperationException("setCharset not supported by HwmfFont.");
|
throw new UnsupportedOperationException("setCharset not supported by HwmfFont.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ height: "+height+
|
||||||
|
", width: "+width+
|
||||||
|
", escapment: "+escapement+
|
||||||
|
", weight: "+weight+
|
||||||
|
", italic: "+italic+
|
||||||
|
", underline: "+underline+
|
||||||
|
", strikeOut: "+strikeOut+
|
||||||
|
", charset: '"+charSet+"'"+
|
||||||
|
", outPrecision: '"+outPrecision+"'"+
|
||||||
|
", clipPrecision: '"+clipPrecision+"'"+
|
||||||
|
", qualtiy: '"+quality+"'"+
|
||||||
|
", pitch: '"+getPitch()+"'"+
|
||||||
|
", family: '"+getFamily()+"'"+
|
||||||
|
", facename: '"+facename+"'"+
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
protected int readString(LittleEndianInputStream leis, StringBuilder sb, int limit) throws IOException {
|
protected int readString(LittleEndianInputStream leis, StringBuilder sb, int limit) throws IOException {
|
||||||
byte buf[] = new byte[limit], b, readBytes = 0;
|
byte buf[] = new byte[limit], b, readBytes = 0;
|
||||||
do {
|
do {
|
||||||
|
|
|
@ -49,6 +49,11 @@ public class HwmfMisc {
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
ctx.saveProperties();
|
ctx.saveProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,7 +85,7 @@ public class HwmfMisc {
|
||||||
* member is positive, nSavedDC represents a specific instance of the state to be restored. If
|
* member is positive, nSavedDC represents a specific instance of the state to be restored. If
|
||||||
* this member is negative, nSavedDC represents an instance relative to the current state.
|
* this member is negative, nSavedDC represents an instance relative to the current state.
|
||||||
*/
|
*/
|
||||||
private int nSavedDC;
|
protected int nSavedDC;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HwmfRecordType getWmfRecordType() {
|
public HwmfRecordType getWmfRecordType() {
|
||||||
|
@ -97,6 +102,11 @@ public class HwmfMisc {
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
ctx.restoreProperties(nSavedDC);
|
ctx.restoreProperties(nSavedDC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ nSavedDC: "+nSavedDC+" }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,7 +115,7 @@ public class HwmfMisc {
|
||||||
*/
|
*/
|
||||||
public static class WmfSetBkColor implements HwmfRecord {
|
public static class WmfSetBkColor implements HwmfRecord {
|
||||||
|
|
||||||
private HwmfColorRef colorRef;
|
protected final HwmfColorRef colorRef = new HwmfColorRef();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HwmfRecordType getWmfRecordType() {
|
public HwmfRecordType getWmfRecordType() {
|
||||||
|
@ -114,7 +124,6 @@ public class HwmfMisc {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
||||||
colorRef = new HwmfColorRef();
|
|
||||||
return colorRef.init(leis);
|
return colorRef.init(leis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +131,11 @@ public class HwmfMisc {
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
ctx.getProperties().setBackgroundColor(colorRef);
|
ctx.getProperties().setBackgroundColor(colorRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ colorRef: "+colorRef+" }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -465,6 +479,11 @@ public class HwmfMisc {
|
||||||
|
|
||||||
ctx.unsetObjectTableEntry(objectIndex);
|
ctx.unsetObjectTableEntry(objectIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ index: "+objectIndex+" }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class WmfCreatePatternBrush implements HwmfRecord, HwmfObjectTableEntry {
|
public static class WmfCreatePatternBrush implements HwmfRecord, HwmfObjectTableEntry {
|
||||||
|
|
|
@ -82,7 +82,7 @@ public class HwmfText {
|
||||||
*/
|
*/
|
||||||
public static class WmfSetTextColor implements HwmfRecord {
|
public static class WmfSetTextColor implements HwmfRecord {
|
||||||
|
|
||||||
private HwmfColorRef colorRef;
|
protected final HwmfColorRef colorRef = new HwmfColorRef();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HwmfRecordType getWmfRecordType() {
|
public HwmfRecordType getWmfRecordType() {
|
||||||
|
@ -91,7 +91,6 @@ public class HwmfText {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
||||||
colorRef = new HwmfColorRef();
|
|
||||||
return colorRef.init(leis);
|
return colorRef.init(leis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +98,11 @@ public class HwmfText {
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
ctx.getProperties().setTextColor(colorRef);
|
ctx.getProperties().setTextColor(colorRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ colorRef: "+colorRef+" }";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -645,5 +649,10 @@ public class HwmfText {
|
||||||
public HwmfFont getFont() {
|
public HwmfFont getFont() {
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{ font: "+font+" } ";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
package org.apache.poi.hwmf.record;
|
package org.apache.poi.hwmf.record;
|
||||||
|
|
||||||
|
import static org.apache.poi.hwmf.record.HwmfDraw.readBounds;
|
||||||
|
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
import java.awt.geom.Area;
|
import java.awt.geom.Area;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
@ -446,20 +448,7 @@ public class HwmfWindowing {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
||||||
// A 16-bit signed integer that defines the y-coordinate, in logical units, of the
|
return readBounds(leis, bounds);
|
||||||
// lower-right corner of the rectangle.
|
|
||||||
final int bottom = leis.readShort();
|
|
||||||
// A 16-bit signed integer that defines the x-coordinate, in logical units, of the
|
|
||||||
// lower-right corner of the rectangle.
|
|
||||||
final int right = leis.readShort();
|
|
||||||
// A 16-bit signed integer that defines the y-coordinate, in logical units, of the
|
|
||||||
// upper-left corner of the rectangle.
|
|
||||||
final int top = leis.readShort();
|
|
||||||
// A 16-bit signed integer that defines the x-coordinate, in logical units, of the
|
|
||||||
// upper-left corner of the rectangle.
|
|
||||||
final int left = leis.readShort();
|
|
||||||
bounds.setRect(left, top, right-left, bottom-top);
|
|
||||||
return 4*LittleEndianConsts.SHORT_SIZE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -470,6 +459,16 @@ public class HwmfWindowing {
|
||||||
@Override
|
@Override
|
||||||
public void applyObject(HwmfGraphics ctx) {
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return
|
||||||
|
"{ x: "+bounds.getX()+
|
||||||
|
", y: "+bounds.getY()+
|
||||||
|
", w: "+bounds.getWidth()+
|
||||||
|
", h: "+bounds.getHeight()+
|
||||||
|
"}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -63,7 +64,7 @@ public class HemfPictureTest {
|
||||||
@Test
|
@Test
|
||||||
@Ignore("Only for manual tests")
|
@Ignore("Only for manual tests")
|
||||||
public void paint() throws IOException {
|
public void paint() throws IOException {
|
||||||
File f = sl_samples.getFile("wrench.emf");
|
File f = new File("picture_14.emf"); // sl_samples.getFile("wrench.emf");
|
||||||
try (FileInputStream fis = new FileInputStream(f)) {
|
try (FileInputStream fis = new FileInputStream(f)) {
|
||||||
HemfPicture emf = new HemfPicture(fis);
|
HemfPicture emf = new HemfPicture(fis);
|
||||||
|
|
||||||
|
@ -84,6 +85,14 @@ public class HemfPictureTest {
|
||||||
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);
|
||||||
|
|
||||||
|
FileWriter fw = new FileWriter("record-list.txt");
|
||||||
|
int i=0;
|
||||||
|
for (HemfRecord r : emf.getRecords()) {
|
||||||
|
fw.write(i + " "+r.getEmfRecordType()+" "+r.toString()+"\n");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
fw.close();
|
||||||
|
|
||||||
emf.draw(g, new Rectangle2D.Double(0,0,width,height));
|
emf.draw(g, new Rectangle2D.Double(0,0,width,height));
|
||||||
|
|
||||||
g.dispose();
|
g.dispose();
|
||||||
|
@ -160,7 +169,7 @@ public class HemfPictureTest {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (HemfRecord record : pic) {
|
for (HemfRecord record : pic) {
|
||||||
if (record.getEmfRecordType().equals(HemfRecordType.exttextoutw)) {
|
if (record.getEmfRecordType().equals(HemfRecordType.exttextoutw)) {
|
||||||
HemfText.ExtTextOutW extTextOutW = (HemfText.ExtTextOutW) record;
|
HemfText.EmfExtTextOutW extTextOutW = (HemfText.EmfExtTextOutW) record;
|
||||||
Point2D reference = extTextOutW.getTextObject().getReference();
|
Point2D reference = extTextOutW.getTextObject().getReference();
|
||||||
if (lastY > -1 && lastY != reference.getY()) {
|
if (lastY > -1 && lastY != reference.getY()) {
|
||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
|
@ -194,7 +203,7 @@ public class HemfPictureTest {
|
||||||
int foundExpected = 0;
|
int foundExpected = 0;
|
||||||
for (HemfRecord record : pic) {
|
for (HemfRecord record : pic) {
|
||||||
if (record.getEmfRecordType().equals(HemfRecordType.exttextoutw)) {
|
if (record.getEmfRecordType().equals(HemfRecordType.exttextoutw)) {
|
||||||
HemfText.ExtTextOutW extTextOutW = (HemfText.ExtTextOutW) record;
|
HemfText.EmfExtTextOutW extTextOutW = (HemfText.EmfExtTextOutW) record;
|
||||||
Point2D reference = extTextOutW.getTextObject().getReference();
|
Point2D reference = extTextOutW.getTextObject().getReference();
|
||||||
if (lastY > -1 && lastY != reference.getY()) {
|
if (lastY > -1 && lastY != reference.getY()) {
|
||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
|
|
Loading…
Reference in New Issue