aboutsummaryrefslogtreecommitdiffstats
path: root/src/scratchpad
diff options
context:
space:
mode:
authorAndreas Beeker <kiwiwings@apache.org>2019-11-29 00:39:35 +0000
committerAndreas Beeker <kiwiwings@apache.org>2019-11-29 00:39:35 +0000
commit1562175343efa0fd12ce6df3d6e887cd78ce61ee (patch)
tree13a9868274a347712e948e3bb5252dd3bd81a768 /src/scratchpad
parent7aab19c3b58f4911bb28ac67df9785e58c8a5734 (diff)
downloadpoi-1562175343efa0fd12ce6df3d6e887cd78ce61ee.tar.gz
poi-1562175343efa0fd12ce6df3d6e887cd78ce61ee.zip
Bug 60656 - Emf image support in slideshows
- use Rectangle2D instead of Dimension2D for image bounds - fix shearing transformation - fix rendering of font attributes (bold/italic/...) - emf+: needs its own object table and properties table - emf+: add linear gradient handler - emf+: handle brush data of pens - wmf/emf/emf+: position right aligned text correctly - emf+: use emf+ instead of emf records in dual-mode - emf+: handle region data and operations correctly - emf/+: map font weight to awt font weight correctly git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1870566 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/scratchpad')
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java40
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java171
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java37
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java4
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java4
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java24
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java3
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordIterator.java6
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java183
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java92
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusFont.java15
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java17
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java45
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java4
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java18
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java26
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java4
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java2
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java150
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java13
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java56
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphicsState.java72
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java12
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBrushStyle.java6
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java4
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRegionMode.java19
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java5
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java40
28 files changed, 879 insertions, 193 deletions
diff --git a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java
index 0d6a8ead1e..5ec47cc054 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java
@@ -17,10 +17,13 @@
package org.apache.poi.hemf.draw;
+import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.function.BiConsumer;
import org.apache.poi.hemf.record.emfplus.HemfPlusBrush.EmfPlusHatchStyle;
@@ -47,6 +50,10 @@ public class HemfDrawProperties extends HwmfDrawProperties {
private final List<AffineTransform> transXForm = new ArrayList<>();
private final List<TransOperand> transOper = new ArrayList<>();
+ private Rectangle2D brushRect;
+ private List<? extends Map.Entry<Float,Color>> brushColorsV;
+ private List<? extends Map.Entry<Float,Color>> brushColorsH;
+
public HemfDrawProperties() {
}
@@ -60,6 +67,15 @@ public class HemfDrawProperties extends HwmfDrawProperties {
emfPlusImage = other.emfPlusImage;
transXForm.addAll(other.transXForm);
transOper.addAll(other.transOper);
+ if (other.brushRect != null) {
+ brushRect = (Rectangle2D)other.brushRect.clone();
+ }
+ if (other.brushColorsV != null) {
+ brushColorsV = new ArrayList<>(other.brushColorsV);
+ }
+ if (other.brushColorsH != null) {
+ brushColorsH = new ArrayList<>(other.brushColorsH);
+ }
}
/**
@@ -139,4 +155,28 @@ public class HemfDrawProperties extends HwmfDrawProperties {
List<TransOperand> getTransOper() {
return transOper;
}
+
+ public Rectangle2D getBrushRect() {
+ return brushRect;
+ }
+
+ public void setBrushRect(Rectangle2D brushRect) {
+ this.brushRect = brushRect;
+ }
+
+ public List<? extends Map.Entry<Float, Color>> getBrushColorsV() {
+ return brushColorsV;
+ }
+
+ public void setBrushColorsV(List<? extends Map.Entry<Float, Color>> brushColorsV) {
+ this.brushColorsV = brushColorsV;
+ }
+
+ public List<? extends Map.Entry<Float, Color>> getBrushColorsH() {
+ return brushColorsH;
+ }
+
+ public void setBrushColorsH(List<? extends Map.Entry<Float, Color>> brushColorsH) {
+ this.brushColorsH = brushColorsH;
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java
index f39e043135..41aa52aea8 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java
@@ -20,16 +20,25 @@ 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.AlphaComposite;
import java.awt.Color;
+import java.awt.Composite;
import java.awt.Graphics2D;
+import java.awt.LinearGradientPaint;
+import java.awt.MultipleGradientPaint;
import java.awt.Paint;
+import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.function.Consumer;
+import java.util.stream.Stream;
import org.apache.poi.hemf.draw.HemfDrawProperties.TransOperand;
import org.apache.poi.hemf.record.emf.HemfComment.EmfComment;
@@ -37,7 +46,9 @@ import org.apache.poi.hemf.record.emf.HemfRecord;
import org.apache.poi.hemf.record.emfplus.HemfPlusRecord;
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
import org.apache.poi.hwmf.draw.HwmfGraphics;
+import org.apache.poi.hwmf.record.HwmfBrushStyle;
import org.apache.poi.hwmf.record.HwmfColorRef;
+import org.apache.poi.hwmf.record.HwmfMisc;
import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
import org.apache.poi.hwmf.record.HwmfPenStyle;
import org.apache.poi.util.Internal;
@@ -58,11 +69,14 @@ public class HemfGraphics extends HwmfGraphics {
private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK);
private EmfRenderState renderState = EmfRenderState.INITIAL;
+ private final Map<Integer,HwmfObjectTableEntry> plusObjectTable = new HashMap<>();
+ private final Map<Integer,HemfDrawProperties> plusPropStack = new HashMap<>();
public HemfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {
super(graphicsCtx,bbox);
// add dummy entry for object ind ex 0, as emf is 1-based
objectIndexes.set(0);
+ getProperties().setBkMode(HwmfMisc.WmfSetBkMode.HwmfBkMode.TRANSPARENT);
}
@Override
@@ -87,14 +101,8 @@ public class HemfGraphics extends HwmfGraphics {
public void draw(HemfRecord r) {
switch (getRenderState()) {
+ default:
case EMF_DCONTEXT:
- // This state specifies that subsequent EMF records encountered in the metafile SHOULD be processed.
- // EMF records cease being processed when the next EMF+ record is encountered.
- if (r instanceof EmfComment) {
- setRenderState(EmfRenderState.EMFPLUS_ONLY);
- }
- r.draw(this);
- break;
case INITIAL:
r.draw(this);
break;
@@ -108,8 +116,6 @@ public class HemfGraphics extends HwmfGraphics {
r.draw(this);
}
break;
- default:
- break;
}
}
@@ -171,10 +177,7 @@ public class HemfGraphics extends HwmfGraphics {
/**
* 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
+ * The index must be &gt; 0
*
* @param entry the record to be stored
* @param index the index to be overwritten, regardless if its content was unset before
@@ -182,42 +185,74 @@ public class HemfGraphics extends HwmfGraphics {
* @see HwmfGraphics#addObjectTableEntry(HwmfObjectTableEntry)
*/
public void addObjectTableEntry(HwmfObjectTableEntry entry, int index) {
- checkTableEntryIndex(index);
+ // in EMF the index must > 0
+ if (index < 1) {
+ throw new IndexOutOfBoundsException("Object table entry index in EMF must be > 0 - invalid index: "+index);
+ }
objectIndexes.set(index);
objectTable.put(index, entry);
}
/**
- * Gets a record which was registered earliser
+ * Adds or sets an record of type {@link HwmfObjectTableEntry} to the plus object table.
+ * The index must be in the range [0..63]
+ *
+ * @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 addPlusObjectTableEntry(HwmfObjectTableEntry entry, int index) {
+ // in EMF+ the index must be between 0 and 63
+ if (index < 0 || index > 63) {
+ throw new IndexOutOfBoundsException("Object table entry index in EMF+ must be [0..63] - invalid index: "+index);
+ }
+ plusObjectTable.put(index, entry);
+ }
+
+ /**
+ * Gets a record which was registered earlier
* @param index the record index
* @return the record or {@code null} if it doesn't exist
*/
public HwmfObjectTableEntry getObjectTableEntry(int index) {
- checkTableEntryIndex(index);
+ // in EMF the index must > 0
+ if (index < 1) {
+ throw new IndexOutOfBoundsException("Object table entry index in EMF must be > 0 - invalid index: "+index);
+ }
return objectTable.get(index);
}
- private void checkTableEntryIndex(int index) {
- if (renderState != EmfRenderState.EMFPLUS_ONLY && renderState != EmfRenderState.EMF_DCONTEXT) {
- // in EMF the index must > 0
- if (index < 1) {
- throw new IndexOutOfBoundsException("Object table entry index in EMF must be > 0 - invalid index: "+index);
- }
- } else {
- // in EMF+ the index must be between 0 and 63
- if (index < 0 || index > 63) {
- throw new IndexOutOfBoundsException("Object table entry index in EMF+ must be [0..63] - invalid index: "+index);
- }
+ public HwmfObjectTableEntry getPlusObjectTableEntry(int index) {
+ // in EMF+ the index must be between 0 and 63
+ if (index < 0 || index > 63) {
+ throw new IndexOutOfBoundsException("Object table entry index in EMF+ must be [0..63] - invalid index: "+index);
}
+ return plusObjectTable.get(index);
}
-
@Override
public void applyObjectTableEntry(int index) {
if ((index & 0x80000000) != 0) {
selectStockObject(index);
} else {
- super.applyObjectTableEntry(index);
+ HwmfObjectTableEntry ote = objectTable.get(index);
+ if (ote == null) {
+ throw new NoSuchElementException("EMF reference exception - object table entry on index "+index+" was deleted before.");
+ }
+ ote.applyObject(this);
+ }
+ }
+
+ public void applyPlusObjectTableEntry(int index) {
+ if ((index & 0x80000000) != 0) {
+ selectStockObject(index);
+ } else {
+ HwmfObjectTableEntry ote = plusObjectTable.get(index);
+ if (ote == null) {
+ throw new NoSuchElementException("EMF+ reference exception - plus object table entry on index "+index+" was deleted before.");
+ }
+ ote.applyObject(this);
}
}
@@ -351,4 +386,82 @@ public class HemfGraphics extends HwmfGraphics {
graphicsCtx.setTransform(tx);
}
+
+ @Override
+ public void fill(Shape shape) {
+ HemfDrawProperties prop = getProperties();
+
+ Composite old = graphicsCtx.getComposite();
+ graphicsCtx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
+ if (prop.getBrushStyle() != HwmfBrushStyle.BS_NULL) {
+ if (prop.getBkMode() == HwmfMisc.WmfSetBkMode.HwmfBkMode.OPAQUE) {
+ graphicsCtx.setPaint(prop.getBackgroundColor().getColor());
+ graphicsCtx.fill(shape);
+ }
+
+ graphicsCtx.setPaint(getFill());
+ graphicsCtx.fill(shape);
+ }
+ graphicsCtx.setComposite(old);
+ }
+
+
+ @Override
+ protected Paint getLinearGradient() {
+ HemfDrawProperties prop = getProperties();
+ Rectangle2D rect = prop.getBrushRect();
+ List<? extends Map.Entry<Float, Color>> colorsH = prop.getBrushColorsH();
+ assert(rect != null && colorsH != null);
+
+ // TODO: handle ColorsV list with a custom GradientPaint
+ // for an idea on how to handle 2d-gradients google "bilinear color interpolation".
+ // basically use two linear interpolations for x/y or vertical/horizontal axis.
+ // the resulting two colors need to be interpolated by 50%.
+
+ return new LinearGradientPaint(
+ new Point2D.Double(rect.getMinX(),rect.getCenterY()),
+ new Point2D.Double(rect.getMaxX(),rect.getCenterY()),
+ toArray(colorsH.stream().map(Map.Entry::getKey), colorsH.size()),
+ colorsH.stream().map(Map.Entry::getValue).toArray(Color[]::new),
+ MultipleGradientPaint.CycleMethod.NO_CYCLE,
+ MultipleGradientPaint.ColorSpaceType.SRGB,
+ prop.getBrushTransform()
+ );
+ }
+
+ private static float[] toArray(Stream<? extends Number> numbers, int size) {
+ float[] arr = new float[size];
+ final int[] i = {0};
+ numbers.forEach(n -> arr[i[0]++] = n.floatValue());
+ return arr;
+ }
+
+ /**
+ * Saves the current properties to the plus stack
+ */
+ public void savePlusProperties(int index) {
+ final HemfDrawProperties p = getProperties();
+ assert(p != null);
+ p.setTransform(graphicsCtx.getTransform());
+ p.setClip(graphicsCtx.getClip());
+ plusPropStack.put(index,p);
+ prop = newProperties(p);
+ }
+
+ /**
+ * Restores the properties from the plus stack
+ *
+ * @param index the index of the previously saved properties
+ */
+ public void restorePlusProperties(int index) {
+ if (!plusPropStack.containsKey(index)) {
+ return;
+ }
+
+ prop = new HemfDrawProperties(plusPropStack.get(index));
+
+ graphicsCtx.setTransform(prop.getTransform());
+ graphicsCtx.setClip(prop.getClip());
+ }
+
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java
index e41fe8d69e..42cef834a9 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java
@@ -19,11 +19,9 @@ package org.apache.poi.hemf.draw;
import static org.apache.poi.hwmf.draw.HwmfImageRenderer.getOuterBounds;
-import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
-import java.awt.Shape;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
@@ -33,6 +31,7 @@ import java.io.InputStream;
import org.apache.poi.common.usermodel.GenericRecord;
import org.apache.poi.hemf.usermodel.HemfPicture;
+import org.apache.poi.hwmf.draw.HwmfGraphicsState;
import org.apache.poi.hwmf.draw.HwmfImageRenderer;
import org.apache.poi.sl.draw.BitmapImageRenderer;
import org.apache.poi.sl.draw.EmbeddedExtractor;
@@ -67,11 +66,6 @@ public class HemfImageRenderer implements ImageRenderer, EmbeddedExtractor {
}
@Override
- public Dimension2D getDimension() {
- return Units.pointsToPixel(image == null ? new Dimension() : image.getSize());
- }
-
- @Override
public void setAlpha(double alpha) {
this.alpha = alpha;
}
@@ -110,20 +104,20 @@ public class HemfImageRenderer implements ImageRenderer, EmbeddedExtractor {
return false;
}
- boolean isClipped = true;
- if (clip == null) {
- isClipped = false;
- clip = new Insets(0,0,0,0);
- }
-
- Shape clipOld = graphics.getClip();
- if (isClipped) {
- graphics.clip(anchor);
- }
+ HwmfGraphicsState graphicsState = new HwmfGraphicsState();
+ graphicsState.backup(graphics);
- image.draw(graphics, getOuterBounds(anchor, clip));
+ try {
+ if (clip != null) {
+ graphics.clip(anchor);
+ } else {
+ clip = new Insets(0, 0, 0, 0);
+ }
- graphics.setClip(clipOld);
+ image.draw(graphics, getOuterBounds(anchor, clip));
+ } finally {
+ graphicsState.restore(graphics);
+ }
return true;
}
@@ -142,4 +136,9 @@ public class HemfImageRenderer implements ImageRenderer, EmbeddedExtractor {
public Rectangle2D getNativeBounds() {
return image.getBounds();
}
+
+ @Override
+ public Rectangle2D getBounds() {
+ return Units.pointsToPixel(image == null ? new Rectangle2D.Double() : image.getBoundsInPoints());
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
index 9129ba4852..d476c21bcc 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
@@ -29,6 +29,7 @@ import java.util.function.Supplier;
import org.apache.poi.common.usermodel.GenericRecord;
import org.apache.poi.hemf.draw.HemfGraphics;
+import org.apache.poi.hemf.draw.HemfGraphics.EmfRenderState;
import org.apache.poi.hemf.record.emfplus.HemfPlusRecord;
import org.apache.poi.hemf.record.emfplus.HemfPlusRecordIterator;
import org.apache.poi.hwmf.usermodel.HwmfPicture;
@@ -300,6 +301,9 @@ public class HemfComment {
@Override
public void draw(HemfGraphics ctx) {
+ // This state specifies that subsequent EMF records encountered in the metafile SHOULD be processed.
+ // EMF records cease being processed when the next EMF+ record is encountered.
+ ctx.setRenderState(EmfRenderState.EMFPLUS_ONLY);
records.forEach(ctx::draw);
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
index 514db1d309..d6b8228ea6 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
@@ -862,7 +862,9 @@ public class HemfFill {
// m12 (translateY) = eDy (The vertical translation component, in logical units.)
double m12 = leis.readFloat();
- xform.setTransform(m00, m10, m01, m11, m02, m12);
+ // TODO: not sure, why the shearing has to be inverted here,
+ // probably because of the different world/user space transformation
+ xform.setTransform(m00, -m10, -m01, m11, m02, m12);
if (xform.isIdentity()) {
xform.setToIdentity();
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java
index f7106053a7..66229fb536 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java
@@ -492,6 +492,30 @@ public class HemfFont extends HwmfFont {
);
}
+ public void setHeight(double height) {
+ this.height = height;
+ }
+
+ public void setWeight(int weight) {
+ this.weight = weight;
+ }
+
+ public void setItalic(boolean italic) {
+ this.italic = italic;
+ }
+
+ public void setUnderline(boolean underline) {
+ this.underline = underline;
+ }
+
+ public void setStrikeOut(boolean strikeOut) {
+ this.strikeOut = strikeOut;
+ }
+
+ public void setTypeface(String typeface) {
+ this.facename = typeface;
+ }
+
@Override
protected int readString(LittleEndianInputStream leis, StringBuilder sb, int limit) throws IOException {
sb.setLength(0);
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java
index 6c43fbdb8f..e9056f682c 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java
@@ -559,6 +559,9 @@ public class HemfMisc {
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
final int startIdx = leis.getReadIndex();
+ // An unsigned integer that specifies the index of the extended logical pen object in
+ // the EMF object table. This index MUST be saved so that this object can be
+ // reused or modified.
penIndex = (int) leis.readUInt();
// A 32-bit unsigned integer that specifies the offset from the start of this
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordIterator.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordIterator.java
index dfa68670e1..dc5a2e3d57 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordIterator.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordIterator.java
@@ -72,10 +72,12 @@ public class HemfRecordIterator implements Iterator<HemfRecord> {
final HemfRecord record = type.constructor.get();
try {
- long remBytes = recordSize-HEADER_SIZE;
+ long remBytes = recordSize - HEADER_SIZE;
long readBytes = record.init(stream, remBytes, recordId);
assert (readBytes <= remBytes);
- stream.skipFully((int)(remBytes-readBytes));
+ stream.skipFully((int) (remBytes - readBytes));
+ } catch (RecordFormatException e) {
+ throw e;
} catch (IOException|RuntimeException e) {
throw new RecordFormatException(e);
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java
index e8e2f4262d..4aeaf43e94 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java
@@ -29,12 +29,18 @@ import java.awt.geom.Rectangle2D;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.AbstractMap;
+import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import org.apache.poi.common.usermodel.GenericRecord;
import org.apache.poi.hemf.draw.HemfDrawProperties;
@@ -47,6 +53,7 @@ import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
import org.apache.poi.hemf.record.emfplus.HemfPlusPath.EmfPlusPath;
import org.apache.poi.hwmf.record.HwmfBrushStyle;
import org.apache.poi.hwmf.record.HwmfColorRef;
+import org.apache.poi.sl.draw.DrawPaint;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.GenericRecordJsonWriter;
@@ -310,7 +317,19 @@ public class HemfPlusBrush {
long init(LittleEndianInputStream leis, long dataSize) throws IOException;
+ /**
+ * Apply brush data to graphics properties
+ * @param ctx the graphics context
+ * @param continuedObjectData the list continued object data
+ */
void applyObject(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData);
+
+ /**
+ * Apply brush data to pen properties
+ * @param ctx the graphics context
+ * @param continuedObjectData the list continued object data
+ */
+ void applyPen(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData);
}
/** The EmfPlusBrush object specifies a graphics brush for filling regions. */
@@ -347,6 +366,13 @@ public class HemfPlusBrush {
brushData.applyObject(ctx, continuedObjectData);
}
+
+ public void applyPen(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
+ EmfPlusBrushData brushData = getBrushData(continuedObjectData);
+ brushData.applyPen(ctx, continuedObjectData);
+ }
+
+
@Override
public EmfPlusGraphicsVersion getGraphicsVersion() {
return graphicsVersion;
@@ -372,7 +398,6 @@ public class HemfPlusBrush {
return brushData;
}
-
public byte[] getRawData(List<? extends EmfPlusObjectData> continuedObjectData) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
@@ -416,12 +441,18 @@ public class HemfPlusBrush {
@Override
public void applyObject(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
HemfDrawProperties prop = ctx.getProperties();
- prop.setBackgroundColor(new HwmfColorRef(solidColor));
+ prop.setBrushColor(new HwmfColorRef(solidColor));
prop.setBrushTransform(null);
prop.setBrushStyle(HwmfBrushStyle.BS_SOLID);
}
@Override
+ public void applyPen(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
+ HemfDrawProperties prop = ctx.getProperties();
+ prop.setPenColor(new HwmfColorRef(solidColor));
+ }
+
+ @Override
public String toString() {
return GenericRecordJsonWriter.marshal(this);
}
@@ -458,6 +489,12 @@ public class HemfPlusBrush {
}
@Override
+ public void applyPen(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
+ HemfDrawProperties prop = ctx.getProperties();
+ prop.setPenColor(new HwmfColorRef(foreColor));
+ }
+
+ @Override
public String toString() {
return GenericRecordJsonWriter.marshal(this);
}
@@ -484,12 +521,15 @@ public class HemfPlusBrush {
private Rectangle2D rect = new Rectangle2D.Double();
private Color startColor, endColor;
private AffineTransform transform;
- private double[] positions;
+ private float[] positions;
private Color[] blendColors;
- private double[] positionsV;
- private double[] blendFactorsV;
- private double[] positionsH;
- private double[] blendFactorsH;
+ private float[] positionsV;
+ private float[] blendFactorsV;
+ private float[] positionsH;
+ private float[] blendFactorsH;
+
+ private static int[] FLAG_MASKS = { 0x02, 0x04, 0x08, 0x10, 0x80 };
+ private static String[] FLAG_NAMES = { "TRANSFORM", "PRESET_COLORS", "BLEND_FACTORS_H", "BLEND_FACTORS_V", "BRUSH_DATA_IS_GAMMA_CORRECTED" };
@Override
public long init(LittleEndianInputStream leis, long dataSize) throws IOException {
@@ -518,16 +558,13 @@ public class HemfPlusBrush {
size += readXForm(leis, (transform = new AffineTransform()));
}
- final boolean isPreset = PRESET_COLORS.isSet(dataFlags);
- final boolean blendH = BLEND_FACTORS_H.isSet(dataFlags);
- final boolean blendV = BLEND_FACTORS_V.isSet(dataFlags);
- if (isPreset && (blendH || blendV)) {
+ if (isPreset() && (isBlendH() || isBlendV())) {
throw new RuntimeException("invalid combination of preset colors and blend factors v/h");
}
- size += (isPreset) ? readColors(leis, d -> positions = d, c -> blendColors = c) : 0;
- size += (blendV) ? readFactors(leis, d -> positionsV = d, f -> blendFactorsV = f) : 0;
- size += (blendH) ? readFactors(leis, d -> positionsH = d, f -> blendFactorsH = f) : 0;
+ size += (isPreset()) ? readColors(leis, d -> positions = d, c -> blendColors = c) : 0;
+ size += (isBlendV()) ? readFactors(leis, d -> positionsV = d, f -> blendFactorsV = f) : 0;
+ size += (isBlendH()) ? readFactors(leis, d -> positionsH = d, f -> blendFactorsH = f) : 0;
return size;
}
@@ -535,7 +572,26 @@ public class HemfPlusBrush {
@Override
public void applyObject(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
HemfDrawProperties prop = ctx.getProperties();
- // TODO: implement
+ prop.setBrushStyle(HwmfBrushStyle.BS_LINEAR_GRADIENT);
+ prop.setBrushRect(rect);
+ prop.setBrushTransform(transform);
+
+ // Preset colors and BlendH/V are mutual exclusive
+ if (isPreset()) {
+ setColorProps(prop::setBrushColorsH, positions, this::getBlendColorAt);
+ } else {
+ setColorProps(prop::setBrushColorsH, positionsH, this::getBlendHColorAt);
+ }
+ setColorProps(prop::setBrushColorsV, positionsV, this::getBlendVColorAt);
+
+ if (!(isPreset() || isBlendH() || isBlendV())) {
+ prop.setBrushColorsH(Arrays.asList(kv(0f,startColor), kv(1f,endColor)));
+ }
+ }
+
+ @Override
+ public void applyPen(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
+
}
@Override
@@ -551,7 +607,7 @@ public class HemfPlusBrush {
@Override
public Map<String, Supplier<?>> getGenericProperties() {
final Map<String,Supplier<?>> m = new LinkedHashMap<>();
- m.put("flags", () -> dataFlags);
+ m.put("flags", GenericRecordUtil.getBitsAsString(() -> dataFlags, FLAG_MASKS, FLAG_NAMES));
m.put("wrapMode", () -> wrapMode);
m.put("rect", () -> rect);
m.put("startColor", () -> startColor);
@@ -565,6 +621,67 @@ public class HemfPlusBrush {
m.put("blendFactorsH", () -> blendFactorsH);
return Collections.unmodifiableMap(m);
}
+
+ private boolean isPreset() {
+ return PRESET_COLORS.isSet(dataFlags);
+ }
+
+ private boolean isBlendH() {
+ return BLEND_FACTORS_H.isSet(dataFlags);
+ }
+
+ private boolean isBlendV() {
+ return BLEND_FACTORS_V.isSet(dataFlags);
+ }
+
+ private Map.Entry<Float,Color> getBlendColorAt(int index) {
+ return kv(positions[index], blendColors[index]);
+ }
+
+ private Map.Entry<Float,Color> getBlendHColorAt(int index) {
+ return kv(positionsH[index],interpolateColors(blendFactorsH[index]));
+ }
+
+ private Map.Entry<Float,Color> getBlendVColorAt(int index) {
+ return kv(positionsV[index],interpolateColors(blendFactorsV[index]));
+ }
+
+ private static Map.Entry<Float,Color> kv(Float position, Color color) {
+ return new AbstractMap.SimpleEntry<>(position, color);
+ }
+
+ private static void setColorProps(
+ Consumer<List<? extends Map.Entry<Float, Color>>> setter, float[] positions, Function<Integer,? extends Map.Entry<Float, Color>> sup) {
+ if (positions == null) {
+ setter.accept(null);
+ } else {
+ setter.accept(IntStream.range(0, positions.length).boxed().map(sup).collect(Collectors.toList()));
+ }
+ }
+
+ private Color interpolateColors(final double factor) {
+ // https://stackoverflow.com/questions/1416560/hsl-interpolation
+
+ final double[] hslStart = DrawPaint.RGB2HSL(startColor);
+ final double[] hslStop = DrawPaint.RGB2HSL(endColor);
+
+ BiFunction<Number,Number,Double> linearInter = (start, stop) ->
+ start.doubleValue()+(stop.doubleValue()-start.doubleValue())*factor;
+
+ double alpha = linearInter.apply(startColor.getAlpha(),endColor.getAlpha());
+ double sat = linearInter.apply(hslStart[1],hslStop[1]);
+ double lum = linearInter.apply(hslStart[2],hslStop[2]);
+
+ double hue1 = (hslStart[0]+hslStop[0])/2.;
+ double hue2 = (hslStart[0]+hslStop[0]+360.)/2.;
+
+ Function<Double,Double> hueDelta = (hue) ->
+ Math.min(Math.abs(hslStart[0]-hue), Math.abs(hslStop[0]-hue));
+
+ double hue = hueDelta.apply(hue1) < hueDelta.apply(hue2) ? hue1 : hue2;
+
+ return DrawPaint.HSL2RGB(hue, sat, lum, alpha/255.);
+ }
}
/** The EmfPlusPathGradientBrushData object specifies a path gradient for a graphics brush. */
@@ -577,9 +694,9 @@ public class HemfPlusBrush {
private EmfPlusPath boundaryPath;
private Point2D[] boundaryPoints;
private AffineTransform transform;
- private double[] positions;
+ private float[] positions;
private Color[] blendColors;
- private double[] blendFactorsH;
+ private float[] blendFactorsH;
private Double focusScaleX, focusScaleY;
@Override
@@ -597,8 +714,12 @@ public class HemfPlusBrush {
// that appears at the center point of the brush. The color of the brush changes gradually from the
// boundary color to the center color as it moves from the boundary to the center point.
centerColor = readARGB(leis.readInt());
-
int size = 3*LittleEndianConsts.INT_SIZE;
+
+ if (wrapMode == null) {
+ return size;
+ }
+
size += readPointF(leis, centerPoint);
// An unsigned 32-bit integer that specifies the number of colors specified in the SurroundingColor field.
@@ -608,10 +729,10 @@ public class HemfPlusBrush {
// An array of SurroundingColorCount EmfPlusARGB objects that specify the colors for discrete points on the
// boundary of the brush.
surroundingColor = new Color[colorCount];
- for (int i=0; i<colorCount; i++) {
+ for (int i = 0; i < colorCount; i++) {
surroundingColor[i] = readARGB(leis.readInt());
}
- size += (colorCount+1) * LittleEndianConsts.INT_SIZE;
+ size += (colorCount + 1) * LittleEndianConsts.INT_SIZE;
// The boundary of the path gradient brush, which is specified by either a path or a closed cardinal spline.
// If the BrushDataPath flag is set in the BrushDataFlags field, this field MUST contain an
@@ -678,6 +799,11 @@ public class HemfPlusBrush {
}
@Override
+ public void applyPen(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
+
+ }
+
+ @Override
public String toString() {
return GenericRecordJsonWriter.marshal(this);
}
@@ -748,6 +874,11 @@ public class HemfPlusBrush {
}
@Override
+ public void applyPen(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
+
+ }
+
+ @Override
public String toString() {
return GenericRecordJsonWriter.marshal(this);
}
@@ -768,11 +899,11 @@ public class HemfPlusBrush {
}
}
- private static int readPositions(LittleEndianInputStream leis, Consumer<double[]> pos) {
+ private static int readPositions(LittleEndianInputStream leis, Consumer<float[]> pos) {
final int count = leis.readInt();
int size = LittleEndianConsts.INT_SIZE;
- double[] positions = new double[count];
+ float[] positions = new float[count];
for (int i=0; i<count; i++) {
positions[i] = leis.readFloat();
size += LittleEndianConsts.INT_SIZE;
@@ -782,7 +913,7 @@ public class HemfPlusBrush {
return size;
}
- private static int readColors(LittleEndianInputStream leis, Consumer<double[]> pos, Consumer<Color[]> cols) {
+ private static int readColors(LittleEndianInputStream leis, Consumer<float[]> pos, Consumer<Color[]> cols) {
int[] count = { 0 };
int size = readPositions(leis, p -> { count[0] = p.length; pos.accept(p); });
Color[] colors = new Color[count[0]];
@@ -793,10 +924,10 @@ public class HemfPlusBrush {
return size + colors.length * LittleEndianConsts.INT_SIZE;
}
- private static int readFactors(LittleEndianInputStream leis, Consumer<double[]> pos, Consumer<double[]> facs) {
+ private static int readFactors(LittleEndianInputStream leis, Consumer<float[]> pos, Consumer<float[]> facs) {
int[] count = { 0 };
int size = readPositions(leis, p -> { count[0] = p.length; pos.accept(p); });
- double[] factors = new double[count[0]];
+ float[] factors = new float[count[0]];
for (int i=0; i<factors.length; i++) {
factors[i] = leis.readFloat();
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java
index 68f91fe859..6e54085f50 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java
@@ -22,7 +22,6 @@ import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
-import java.awt.geom.Dimension2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
@@ -34,9 +33,11 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.PrimitiveIterator.OfInt;
import java.util.function.BiFunction;
import java.util.function.Supplier;
+import org.apache.commons.codec.Charsets;
import org.apache.commons.math3.linear.LUDecomposition;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RealMatrix;
@@ -47,7 +48,9 @@ import org.apache.poi.hemf.record.emfplus.HemfPlusMisc.EmfPlusObjectId;
import org.apache.poi.hwmf.record.HwmfBrushStyle;
import org.apache.poi.hwmf.record.HwmfColorRef;
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
+import org.apache.poi.hwmf.record.HwmfPenStyle;
import org.apache.poi.hwmf.record.HwmfTernaryRasterOp;
+import org.apache.poi.hwmf.record.HwmfText;
import org.apache.poi.sl.draw.ImageRenderer;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
@@ -165,7 +168,7 @@ public class HemfPlusDraw {
prop.setBrushStyle(HwmfBrushStyle.BS_SOLID);
prop.setBrushColor(new HwmfColorRef(getSolidColor()));
} else {
- ctx.applyObjectTableEntry(getBrushId());
+ ctx.applyPlusObjectTableEntry(getBrushId());
}
}
}
@@ -208,8 +211,8 @@ public class HemfPlusDraw {
@Override
public void draw(HemfGraphics ctx) {
- ctx.applyObjectTableEntry(penId);
- ctx.applyObjectTableEntry(getObjectId());
+ ctx.applyPlusObjectTableEntry(penId);
+ ctx.applyPlusObjectTableEntry(getObjectId());
HemfDrawProperties prop = ctx.getProperties();
final Path2D path = prop.getPath();
@@ -282,11 +285,18 @@ public class HemfPlusDraw {
@Override
public void draw(HemfGraphics ctx) {
+ HemfDrawProperties prop = ctx.getProperties();
applyColor(ctx);
Area area = new Area();
rectData.stream().map(Area::new).forEach(area::add);
- ctx.fill(area);
+ HwmfPenStyle ps = prop.getPenStyle();
+ try {
+ prop.setPenStyle(null);
+ ctx.fill(area);
+ } finally {
+ prop.setPenStyle(ps);
+ }
}
@Override
@@ -319,6 +329,7 @@ public class HemfPlusDraw {
}
}
+ /** The EmfPlusDrawImagePoints record specifies drawing a scaled image inside a parallelogram. */
public static class EmfPlusDrawImagePoints implements HemfPlusRecord, EmfPlusObjectId, EmfPlusCompressed, EmfPlusRelativePosition {
/**
* This bit indicates that the rendering of the image includes applying an effect.
@@ -426,39 +437,31 @@ public class HemfPlusDraw {
public void draw(HemfGraphics ctx) {
HemfDrawProperties prop = ctx.getProperties();
- ctx.applyObjectTableEntry(imageAttributesID);
- ctx.applyObjectTableEntry(getObjectId());
+ ctx.applyPlusObjectTableEntry(imageAttributesID);
+ ctx.applyPlusObjectTableEntry(getObjectId());
final ImageRenderer ir = prop.getEmfPlusImage();
if (ir == null) {
return;
}
-
AffineTransform txSaved = ctx.getTransform();
- AffineTransform tx = new AffineTransform(txSaved);
+ AffineTransform tx = (AffineTransform)txSaved.clone();
+ HwmfTernaryRasterOp oldOp = prop.getRasterOp();
+ HwmfBkMode oldBk = prop.getBkMode();
try {
tx.concatenate(trans);
ctx.setTransform(tx);
- final Rectangle2D srcBounds = ir.getNativeBounds();
- final Dimension2D dim = ir.getDimension();
-
prop.setRasterOp(HwmfTernaryRasterOp.SRCCOPY);
prop.setBkMode(HwmfBkMode.TRANSPARENT);
- // the buffered image might be rescaled, so we need to calculate a new src rect to take
- // the image data from
- final AffineTransform srcTx = new AffineTransform();
- srcTx.translate(-srcBounds.getX(), srcBounds.getY());
- srcTx.scale(dim.getWidth()/srcBounds.getWidth(), dim.getHeight()/srcBounds.getHeight());
-
- final Rectangle2D biRect = srcTx.createTransformedShape(srcRect).getBounds2D();
-
- // TODO: handle srcUnit
- Rectangle2D destRect = new Rectangle2D.Double(0, 0, biRect.getWidth(), biRect.getHeight());
- ctx.drawImage(ir, srcRect, destRect);
+ // transformation from srcRect to destRect was already applied,
+ // therefore use srcRect as third parameter
+ ctx.drawImage(ir, srcRect, srcRect);
} finally {
+ prop.setBkMode(oldBk);
+ prop.setRasterOp(oldOp);
ctx.setTransform(txSaved);
}
}
@@ -529,8 +532,8 @@ public class HemfPlusDraw {
@Override
public void draw(HemfGraphics ctx) {
- ctx.applyObjectTableEntry(imageAttributesID);
- ctx.applyObjectTableEntry(getObjectId());
+ ctx.applyPlusObjectTableEntry(imageAttributesID);
+ ctx.applyPlusObjectTableEntry(getObjectId());
HemfDrawProperties prop = ctx.getProperties();
prop.setRasterOp(HwmfTernaryRasterOp.SRCCOPY);
@@ -592,7 +595,7 @@ public class HemfPlusDraw {
@Override
public void draw(HemfGraphics ctx) {
applyColor(ctx);
- ctx.applyObjectTableEntry(getObjectId());
+ ctx.applyPlusObjectTableEntry(getObjectId());
HemfDrawProperties prop = ctx.getProperties();
ctx.fill(prop.getPath());
}
@@ -657,12 +660,12 @@ public class HemfPlusDraw {
private int brushId;
private int optionsFlags;
private String glyphs;
- private final List<Point2D> glpyhPos = new ArrayList<>();
+ private final List<Point2D> glyphPos = new ArrayList<>();
private final AffineTransform transformMatrix = new AffineTransform();
@Override
public HemfPlusRecordType getEmfPlusRecordType() {
- return HemfPlusRecordType.drawDriverstring;
+ return HemfPlusRecordType.drawDriverString;
}
@Override
@@ -689,7 +692,7 @@ public class HemfPlusDraw {
// A 32-bit unsigned integer that specifies whether a transform matrix is present in the
// TransformMatrix field.
- boolean hasMatrix = leis.readInt() == 1;
+ int matrixPresent = leis.readInt();
// A 32-bit unsigned integer that specifies number of glyphs in the string.
int glyphCount = leis.readInt();
@@ -716,10 +719,10 @@ public class HemfPlusDraw {
for (int i=0; i<glyphCount; i++) {
Point2D p = new Point2D.Double();
size += readPointF(leis, p);
- glpyhPos.add(p);
+ glyphPos.add(p);
}
- if (hasMatrix) {
+ if (matrixPresent != 0) {
size += HemfFill.readXForm(leis, transformMatrix);
}
@@ -728,6 +731,31 @@ public class HemfPlusDraw {
}
@Override
+ public void draw(HemfGraphics ctx) {
+ HemfDrawProperties prop = ctx.getProperties();
+ prop.setTextAlignLatin(HwmfText.HwmfTextAlignment.LEFT);
+ prop.setTextVAlignLatin(HwmfText.HwmfTextVerticalAlignment.BASELINE);
+
+ ctx.applyPlusObjectTableEntry(getObjectId());
+ if (isSolidColor()) {
+ prop.setTextColor(new HwmfColorRef(getSolidColor()));
+ } else {
+ ctx.applyPlusObjectTableEntry(getBrushId());
+ }
+
+ if (REALIZED_ADVANCE.isSet(optionsFlags)) {
+ byte[] buf = glyphs.getBytes(Charsets.UTF_16LE);
+ ctx.drawString(buf, buf.length, glyphPos.get(0), null, null, null, null, true);
+ } else {
+ final OfInt glyphIter = glyphs.codePoints().iterator();
+ glyphPos.forEach(p -> {
+ byte[] buf = new String(new int[]{glyphIter.next()}, 0, 1).getBytes(Charsets.UTF_16LE);
+ ctx.drawString(buf, buf.length, p, null, null, null, null, true);
+ });
+ }
+ }
+
+ @Override
public String toString() {
return GenericRecordJsonWriter.marshal(this);
}
@@ -739,7 +767,7 @@ public class HemfPlusDraw {
"brushId", this::getBrushId,
"optionsFlags", getBitsAsString(() -> optionsFlags, OPTIONS_MASK, OPTIONS_NAMES),
"glyphs", () -> glyphs,
- "glyphPos", () -> glpyhPos,
+ "glyphPos", () -> glyphPos,
"transform", () -> transformMatrix
);
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusFont.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusFont.java
index e0a2fc759f..857a8e93f5 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusFont.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusFont.java
@@ -22,7 +22,10 @@ import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
+import org.apache.poi.common.usermodel.fonts.FontHeader;
+import org.apache.poi.hemf.draw.HemfDrawProperties;
import org.apache.poi.hemf.draw.HemfGraphics;
+import org.apache.poi.hemf.record.emf.HemfFont;
import org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusUnitType;
import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
@@ -106,7 +109,17 @@ public class HemfPlusFont {
@Override
public void applyObject(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
-
+ HemfDrawProperties prop = ctx.getProperties();
+ HemfFont font = new HemfFont();
+ font.initDefaults();
+ font.setTypeface(family);
+ // TODO: check how to calculate the font size
+ font.setHeight(emSize);
+ font.setStrikeOut(STRIKEOUT.isSet(styleFlags));
+ font.setUnderline(UNDERLINE.isSet(styleFlags));
+ font.setWeight(BOLD.isSet(styleFlags) ? 700 : FontHeader.REGULAR_WEIGHT);
+ font.setItalic(ITALIC.isSet(styleFlags));
+ prop.setFont(font);
}
@Override
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java
index 4fcfeb7d0a..256c72ea49 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java
@@ -18,7 +18,7 @@
package org.apache.poi.hemf.record.emfplus;
-import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+import static org.apache.poi.util.GenericRecordUtil.getEnumBitsAsString;
import java.io.IOException;
import java.util.Map;
@@ -60,8 +60,13 @@ public class HemfPlusHeader implements HemfPlusRecord {
}
}
- private static final int[] FLAGS_MASK = { 0x0001 };
- private static final String[] FLAGS_NAMES = { "DUAL_MODE" };
+ private static final int[] FLAGS_MASK = { 0x0000, 0x0001 };
+ private static final String[] FLAGS_NAMES = { "EMF_PLUS_MODE", "DUAL_MODE" };
+
+ private static final int[] EMFFLAGS_MASK = { 0x0000, 0x0001 };
+ private static final String[] EMFFLAGS_NAMES = { "CONTEXT_PRINTER", "CONTEXT_VIDEO" };
+
+
private int flags;
private final EmfPlusGraphicsVersion version = new EmfPlusGraphicsVersion();
@@ -125,7 +130,7 @@ public class HemfPlusHeader implements HemfPlusRecord {
public void draw(HemfGraphics ctx) {
// currently EMF is better supported than EMF+ ... so if there's a complete set of EMF records available,
// disable EMF+ rendering for now
- ctx.setRenderState(isEmfPlusDualMode() ? EmfRenderState.EMF_ONLY : EmfRenderState.EMF_DCONTEXT);
+ ctx.setRenderState(EmfRenderState.EMF_DCONTEXT);
}
@Override
@@ -136,9 +141,9 @@ public class HemfPlusHeader implements HemfPlusRecord {
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
- "flags", this::getFlags,
+ "flags", getEnumBitsAsString(this::getFlags, FLAGS_MASK, FLAGS_NAMES),
"version", this::getVersion,
- "emfPlusFlags", getBitsAsString(this::getEmfPlusFlags, FLAGS_MASK, FLAGS_NAMES),
+ "emfPlusFlags", getEnumBitsAsString(this::getEmfPlusFlags, EMFFLAGS_MASK, EMFFLAGS_NAMES),
"logicalDpiX", this::getLogicalDpiX,
"logicalDpiY", this::getLogicalDpiY
);
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java
index f3ad69aae5..2a8866be48 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java
@@ -20,6 +20,7 @@ package org.apache.poi.hemf.record.emfplus;
import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readRectF;
import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
@@ -30,6 +31,7 @@ import java.util.function.Supplier;
import org.apache.poi.hemf.draw.HemfDrawProperties;
import org.apache.poi.hemf.draw.HemfGraphics;
import org.apache.poi.hemf.record.emf.HemfFill;
+import org.apache.poi.hwmf.record.HwmfRegionMode;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.GenericRecordUtil;
@@ -53,18 +55,20 @@ public class HemfPlusMisc {
}
public enum CombineMode {
- CombineModeReplace(0x00000000),
- CombineModeIntersect(0x00000001),
- CombineModeUnion(0x00000002),
- CombineModeXOR(0x00000003),
- CombineModeExclude(0x00000004),
- CombineModeComplement(0x00000005)
+ REPLACE(0x00000000, HwmfRegionMode.RGN_COPY),
+ INTERSECT(0x00000001, HwmfRegionMode.RGN_AND),
+ UNION(0x00000002, HwmfRegionMode.RGN_OR),
+ XOR(0x00000003, HwmfRegionMode.RGN_XOR),
+ EXCLUDE(0x00000004, HwmfRegionMode.RGN_DIFF),
+ COMPLEMENT(0x00000005, HwmfRegionMode.RGN_COMPLEMENT)
;
public final int id;
+ public final HwmfRegionMode regionMode;
- CombineMode(int id) {
+ CombineMode(int id, HwmfRegionMode regionMode) {
this.id = id;
+ this.regionMode = regionMode;
}
public static CombineMode valueOf(int id) {
@@ -303,6 +307,14 @@ public class HemfPlusMisc {
public CombineMode getCombineMode() {
return CombineMode.valueOf(COMBINE_MODE.getValue(getFlags()));
}
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ HemfDrawProperties prop = ctx.getProperties();
+ ctx.applyPlusObjectTableEntry(getObjectId());
+ Shape clip = prop.getPath();
+ ctx.setClip(clip, clip == null ? HwmfRegionMode.RGN_COPY : getCombineMode().regionMode, false);
+ }
}
/** The EmfPlusSetClipRect record combines the current clipping region with a rectangle. */
@@ -390,6 +402,11 @@ public class HemfPlusMisc {
}
@Override
+ public void draw(HemfGraphics ctx) {
+ ctx.savePlusProperties(getStackIndex());
+ }
+
+ @Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"flags", this::getFlags,
@@ -407,6 +424,11 @@ public class HemfPlusMisc {
public HemfPlusRecordType getEmfPlusRecordType() {
return HemfPlusRecordType.restore;
}
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ ctx.restorePlusProperties(getStackIndex());
+ }
}
/** The EmfPlusSetRenderingOrigin record specifies the rendering origin for graphics output. */
@@ -432,10 +454,11 @@ public class HemfPlusMisc {
public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
this.flags = flags;
- // A 32-bit unsigned integer that defines the horizontal coordinate value of the rendering origin.
- double x = leis.readUInt();
- // A 32-bit unsigned integer that defines the vertical coordinate value of the rendering origin.
- double y = leis.readUInt();
+ // error in the MS-EMFPLUS docs - its a signed integer instead of an unsigned
+ // A 32-bit signed integer that defines the horizontal coordinate value of the rendering origin.
+ int x = leis.readInt();
+ // A 32-bit signed integer that defines the vertical coordinate value of the rendering origin.
+ int y = leis.readInt();
origin.setLocation(x,y);
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java
index 34c6b40067..06335f0ad0 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java
@@ -196,7 +196,7 @@ public class HemfPlusObject {
public void draw(HemfGraphics ctx) {
if (objectData.isContinuedRecord()) {
EmfPlusObject other;
- HwmfObjectTableEntry entry = ctx.getObjectTableEntry(getObjectId());
+ HwmfObjectTableEntry entry = ctx.getPlusObjectTableEntry(getObjectId());
if (entry instanceof EmfPlusObject &&
objectData.getClass().isInstance((other = (EmfPlusObject)entry).getObjectData())
) {
@@ -205,7 +205,7 @@ public class HemfPlusObject {
throw new RuntimeException("can't find previous record for continued record");
}
} else {
- ctx.addObjectTableEntry(this, getObjectId());
+ ctx.addPlusObjectTableEntry(this, getObjectId());
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java
index 71d71b73eb..ef29a9d63d 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java
@@ -35,6 +35,7 @@ import org.apache.poi.hemf.draw.HemfDrawProperties;
import org.apache.poi.hemf.draw.HemfGraphics;
import org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusCompressed;
import org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusRelativePosition;
+import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
import org.apache.poi.util.BitField;
@@ -87,7 +88,7 @@ public class HemfPlusPath {
private static final int[] TYPE_MASKS = { 0x10, 0x20, 0x80 };
private static final String[] TYPE_NAMES = { "DASHED", "MARKER", "CLOSE" };
- private final HemfPlusHeader.EmfPlusGraphicsVersion graphicsVersion = new HemfPlusHeader.EmfPlusGraphicsVersion();
+ private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
private int pointFlags;
private Point2D[] pathPoints;
private byte[] pointTypes;
@@ -143,7 +144,7 @@ public class HemfPlusPath {
}
@Override
- public HemfPlusHeader.EmfPlusGraphicsVersion getGraphicsVersion() {
+ public EmfPlusGraphicsVersion getGraphicsVersion() {
return graphicsVersion;
}
@@ -175,9 +176,15 @@ public class HemfPlusPath {
@Override
public void applyObject(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
HemfDrawProperties prop = ctx.getProperties();
- Path2D path = new Path2D.Double(Path2D.WIND_NON_ZERO);
- prop.setPath(path);
+ prop.setPath(getPath());
+ }
+
+ public Path2D getPath() {
+ return getPath(Path2D.WIND_NON_ZERO);
+ }
+ public Path2D getPath(int windingRule) {
+ Path2D path = new Path2D.Double(windingRule);
for (int idx=0; idx < pathPoints.length; idx++) {
Point2D p1 = pathPoints[idx];
switch (getPointType(idx)) {
@@ -198,10 +205,11 @@ public class HemfPlusPath {
path.closePath();
}
}
+ return path;
}
@Override
- public EmfPlusObjectType getGenericRecordType() {
+ public Enum getGenericRecordType() {
return EmfPlusObjectType.PATH;
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java
index 4540aab3b0..7f03fb37d2 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java
@@ -34,6 +34,7 @@ import java.util.function.Supplier;
import org.apache.poi.common.usermodel.GenericRecord;
import org.apache.poi.hemf.draw.HemfDrawProperties;
import org.apache.poi.hemf.draw.HemfGraphics;
+import org.apache.poi.hemf.record.emfplus.HemfPlusBrush.EmfPlusBrush;
import org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusUnitType;
import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
@@ -330,6 +331,8 @@ public class HemfPlusPen {
private EmfPlusCustomLineCap customStartCap;
private EmfPlusCustomLineCap customEndCap;
+ private final EmfPlusBrush brush = new EmfPlusBrush();
+
@Override
public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
// An EmfPlusGraphicsVersion object that specifies the version of operating system graphics that
@@ -463,6 +466,8 @@ public class HemfPlusPen {
size += initCustomCap(c -> customEndCap = c, leis);
}
+ size += brush.init(leis, dataSize-size, EmfPlusObjectType.BRUSH, 0);
+
return size;
}
@@ -472,8 +477,12 @@ public class HemfPlusPen {
}
private long initCustomCap(Consumer<EmfPlusCustomLineCap> setter, LittleEndianInputStream leis) throws IOException {
+ int CustomStartCapSize = leis.readInt();
+ int size = LittleEndianConsts.INT_SIZE;
+
EmfPlusGraphicsVersion version = new EmfPlusGraphicsVersion();
- long size = version.init(leis);
+ size += version.init(leis);
+ assert(version.getGraphicsVersion() != null);
boolean adjustableArrow = (leis.readInt() != 0);
size += LittleEndianConsts.INT_SIZE;
@@ -492,11 +501,11 @@ public class HemfPlusPen {
// TOOD:
// - set width according unit type
// - provide logic for different start and end cap
- // - provide standard caps like diamondd
+ // - provide standard caps like diamond
// - support custom caps
- // workaround for too wide pens ... just arbitrary reduce high values ...
- prop.setPenWidth(penWidth > 20 ? 1 : penWidth);
+ brush.applyPen(ctx, continuedObjectData);
+ prop.setPenWidth(penWidth);
prop.setPenStyle(new HwmfPenStyle(){
@Override
public HwmfLineCap getLineCap() {
@@ -573,6 +582,7 @@ public class HemfPlusPen {
m.put("compoundLineData", () -> compoundLineData);
m.put("customStartCap", () -> customStartCap);
m.put("customEndCap", () -> customEndCap);
+ m.put("brush", () -> brush);
return Collections.unmodifiableMap(m);
}
}
@@ -645,13 +655,17 @@ public class HemfPlusPen {
size += readPointF(leis, lineHotSpot);
if (FILL_PATH.isSet(dataFlags)) {
+ int fillSize = leis.readInt();
+ size += LittleEndianConsts.INT_SIZE;
fillPath = new EmfPlusPath();
- size += fillPath.init(leis, -1, null, -1);
+ size += fillPath.init(leis, fillSize, EmfPlusObjectType.PATH, -1);
}
if (LINE_PATH.isSet(dataFlags)) {
+ int pathSize = leis.readInt();
+ size += LittleEndianConsts.INT_SIZE;
outlinePath = new EmfPlusPath();
- size += outlinePath.init(leis, -1, null, -1);
+ size += outlinePath.init(leis, pathSize, EmfPlusObjectType.PATH, -1);
}
return size;
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java
index 608bba7e7f..af1cf0935f 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java
@@ -55,4 +55,8 @@ public interface HemfPlusRecord extends GenericRecord {
default void draw(HemfGraphics ctx) {
}
+ @Override
+ default Enum getGenericRecordType() {
+ return getEmfPlusRecordType();
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java
index 38263f4eff..acbba48668 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java
@@ -76,7 +76,7 @@ public enum HemfPlusRecordType {
setClipRegion(0x4033, HemfPlusMisc.EmfPlusSetClipRegion::new),
setClipPath(0x4034, HemfPlusMisc.EmfPlusSetClipPath::new),
offsetClip(0x4035, UnimplementedHemfPlusRecord::new),
- drawDriverstring(0x4036, HemfPlusDraw.EmfPlusDrawDriverString::new),
+ drawDriverString(0x4036, HemfPlusDraw.EmfPlusDrawDriverString::new),
strokeFillPath(0x4037, UnimplementedHemfPlusRecord::new),
serializableObject(0x4038, UnimplementedHemfPlusRecord::new),
setTSGraphics(0x4039, UnimplementedHemfPlusRecord::new),
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java
index f8e43fc646..8862e856b9 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java
@@ -19,14 +19,19 @@ package org.apache.poi.hemf.record.emfplus;
import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readRectF;
+import java.awt.Shape;
+import java.awt.geom.Area;
+import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.List;
import java.util.Map;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.apache.poi.common.usermodel.GenericRecord;
+import org.apache.poi.hemf.draw.HemfDrawProperties;
import org.apache.poi.hemf.draw.HemfGraphics;
import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
@@ -42,49 +47,51 @@ public class HemfPlusRegion {
* Specifies a region node with child nodes. A Boolean AND operation SHOULD be applied to the left and right
* child nodes specified by an EmfPlusRegionNodeChildNodes object
*/
- AND(0X00000001, EmfPlusRegionNode::new),
+ AND(0X00000001, EmfPlusRegionNode::new, Area::intersect),
/**
* Specifies a region node with child nodes. A Boolean OR operation SHOULD be applied to the left and right
* child nodes specified by an EmfPlusRegionNodeChildNodes object.
*/
- OR(0X00000002, EmfPlusRegionNode::new),
+ OR(0X00000002, EmfPlusRegionNode::new, Area::add),
/**
* Specifies a region node with child nodes. A Boolean XOR operation SHOULD be applied to the left and right
* child nodes specified by an EmfPlusRegionNodeChildNodes object.
*/
- XOR(0X00000003, EmfPlusRegionNode::new),
+ XOR(0X00000003, EmfPlusRegionNode::new, Area::exclusiveOr),
/**
* Specifies a region node with child nodes. A Boolean operation, defined as "the part of region 1 that is excluded
* from region 2", SHOULD be applied to the left and right child nodes specified by an EmfPlusRegionNodeChildNodes object.
*/
- EXCLUDE(0X00000004, EmfPlusRegionNode::new),
+ EXCLUDE(0X00000004, EmfPlusRegionNode::new, Area::subtract),
/**
* Specifies a region node with child nodes. A Boolean operation, defined as "the part of region 2 that is excluded
* from region 1", SHOULD be applied to the left and right child nodes specified by an EmfPlusRegionNodeChildNodes object.
*/
- COMPLEMENT(0X00000005, EmfPlusRegionNode::new),
+ COMPLEMENT(0X00000005, EmfPlusRegionNode::new, Area::subtract),
/**
* Specifies a region node with no child nodes.
* The RegionNodeData field SHOULD specify a boundary with an EmfPlusRectF object.
*/
- RECT(0X10000000, EmfPlusRegionRect::new),
+ RECT(0X10000000, EmfPlusRegionRect::new, null),
/**
* Specifies a region node with no child nodes.
* The RegionNodeData field SHOULD specify a boundary with an EmfPlusRegionNodePath object
*/
- PATH(0X10000001, EmfPlusRegionPath::new),
+ PATH(0X10000001, EmfPlusRegionPath::new, null),
/** Specifies a region node with no child nodes. The RegionNodeData field SHOULD NOT be present. */
- EMPTY(0X10000002, EmfPlusRegionEmpty::new),
+ EMPTY(0X10000002, EmfPlusRegionEmpty::new, null),
/** Specifies a region node with no child nodes, and its bounds are not defined. */
- INFINITE(0X10000003, EmfPlusRegionInfinite::new)
+ INFINITE(0X10000003, EmfPlusRegionInfinite::new, null)
;
public final int id;
public final Supplier<EmfPlusRegionNodeData> constructor;
+ public final BiConsumer<Area,Area> operation;
- EmfPlusRegionNodeDataType(int id, Supplier<EmfPlusRegionNodeData> constructor) {
+ EmfPlusRegionNodeDataType(int id, Supplier<EmfPlusRegionNodeData> constructor, BiConsumer<Area,Area> operation) {
this.id = id;
this.constructor = constructor;
+ this.operation = operation;
}
public static EmfPlusRegionNodeDataType valueOf(int id) {
@@ -95,6 +102,7 @@ public class HemfPlusRegion {
}
}
+ /** The EmfPlusRegion object specifies line and curve segments that define a nonrectilinear shape. */
public static class EmfPlusRegion implements EmfPlusObjectData {
private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
@@ -111,14 +119,16 @@ public class HemfPlusRegion {
// An array of RegionNodeCount+1 EmfPlusRegionNode objects. Regions are specified as a binary tree of
// region nodes, and each node MUST either be a terminal node or specify one or two child nodes.
// RegionNode MUST contain at least one element.
- size += readNode(leis, d -> regionNode = d);
+ size += readNode(leis, this::setRegionNode);
return size;
}
@Override
public void applyObject(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
-
+ HemfDrawProperties prop = ctx.getProperties();
+ Shape shape = regionNode.getShape();
+ prop.setPath(shape == null ? null : new Path2D.Double(shape));
}
@Override
@@ -131,11 +141,19 @@ public class HemfPlusRegion {
return EmfPlusObjectType.REGION;
}
+ private void setRegionNode(EmfPlusRegionNodeData regionNode) {
+ this.regionNode = regionNode;
+ }
+
+ public EmfPlusRegionNodeData getRegionNode() {
+ return regionNode;
+ }
+
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"graphicsVersion", this::getGraphicsVersion,
- "regionNode", () -> regionNode
+ "regionNode", this::getRegionNode
);
}
}
@@ -143,6 +161,8 @@ public class HemfPlusRegion {
public interface EmfPlusRegionNodeData extends GenericRecord {
long init(LittleEndianInputStream leis) throws IOException;
+ Shape getShape();
+ default void setNodeType(EmfPlusRegionNodeDataType type) {}
}
public static class EmfPlusRegionPath extends EmfPlusPath implements EmfPlusRegionNodeData {
@@ -150,6 +170,16 @@ public class HemfPlusRegion {
int dataSize = leis.readInt();
return super.init(leis, dataSize, EmfPlusObjectType.PATH, 0) + LittleEndianConsts.INT_SIZE;
}
+
+ @Override
+ public Shape getShape() {
+ return getPath();
+ }
+
+ @Override
+ public EmfPlusRegionNodeDataType getGenericRecordType() {
+ return EmfPlusRegionNodeDataType.PATH;
+ }
}
public static class EmfPlusRegionInfinite implements EmfPlusRegionNodeData {
@@ -162,6 +192,16 @@ public class HemfPlusRegion {
public Map<String, Supplier<?>> getGenericProperties() {
return null;
}
+
+ @Override
+ public Shape getShape() {
+ return null;
+ }
+
+ @Override
+ public EmfPlusRegionNodeDataType getGenericRecordType() {
+ return EmfPlusRegionNodeDataType.INFINITE;
+ }
}
public static class EmfPlusRegionEmpty implements EmfPlusRegionNodeData {
@@ -174,6 +214,16 @@ public class HemfPlusRegion {
public Map<String, Supplier<?>> getGenericProperties() {
return null;
}
+
+ @Override
+ public Shape getShape() {
+ return new Rectangle2D.Double(0,0,0,0);
+ }
+
+ @Override
+ public EmfPlusRegionNodeDataType getGenericRecordType() {
+ return EmfPlusRegionNodeDataType.EMPTY;
+ }
}
public static class EmfPlusRegionRect implements EmfPlusRegionNodeData {
@@ -188,25 +238,90 @@ public class HemfPlusRegion {
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties("rect", () -> rect);
}
+
+ @Override
+ public Shape getShape() {
+ return rect;
+ }
+
+ @Override
+ public EmfPlusRegionNodeDataType getGenericRecordType() {
+ return EmfPlusRegionNodeDataType.RECT;
+ }
}
+ /** The EmfPlusRegionNode object specifies nodes of a graphics region. */
public static class EmfPlusRegionNode implements EmfPlusRegionNodeData {
private EmfPlusRegionNodeData left, right;
+ private EmfPlusRegionNodeDataType nodeType;
@Override
public long init(LittleEndianInputStream leis) throws IOException {
- long size = readNode(leis, n -> left = n);
- size += readNode(leis, n -> right = n);
+ long size = readNode(leis, this::setLeft);
+ size += readNode(leis, this::setRight);
return size;
}
+ private void setLeft(EmfPlusRegionNodeData left) {
+ this.left = left;
+ }
+
+ private void setRight(EmfPlusRegionNodeData right) {
+ this.right = right;
+ }
+
+ public EmfPlusRegionNodeData getLeft() {
+ return left;
+ }
+
+ public EmfPlusRegionNodeData getRight() {
+ return right;
+ }
+
+ public EmfPlusRegionNodeDataType getNodeType() {
+ return nodeType;
+ }
+
+ @Override
+ public void setNodeType(EmfPlusRegionNodeDataType nodeType) {
+ this.nodeType = nodeType;
+ }
+
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
- "left", () -> left,
- "right", () -> right
+ "nodeType", this::getNodeType,
+ "left", this::getLeft,
+ "right", this::getRight
);
}
+
+ @Override
+ public Shape getShape() {
+ boolean com = (nodeType == EmfPlusRegionNodeDataType.COMPLEMENT);
+ final Shape leftShape = (com ? right : left).getShape();
+ final Shape rightShape = (com ? left : right).getShape();
+
+ if (leftShape == null) {
+ return rightShape;
+ } else if (rightShape == null) {
+ return leftShape;
+ }
+
+ // TODO: check Area vs. Path manipulation
+ Area leftArea = new Area(leftShape);
+ Area rightArea = new Area(rightShape);
+
+ assert(nodeType.operation != null);
+ nodeType.operation.accept(leftArea, rightArea);
+
+ return leftArea;
+ }
+
+ @Override
+ public EmfPlusRegionNodeDataType getGenericRecordType() {
+ return nodeType;
+ }
}
private static long readNode(LittleEndianInputStream leis, Consumer<EmfPlusRegionNodeData> con) throws IOException {
@@ -216,6 +331,7 @@ public class HemfPlusRegion {
assert(type != null);
EmfPlusRegionNodeData nd = type.constructor.get();
con.accept(nd);
+ nd.setNodeType(type);
return LittleEndianConsts.INT_SIZE + nd.init(leis);
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java b/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java
index 547ba87f4c..ed87177882 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java
@@ -131,13 +131,22 @@ public class HemfPicture implements Iterable<HemfRecord>, GenericRecord {
}
/**
+ * Return the image bounds in points
+ *
+ * @return the image bounds in points
+ */
+ public Rectangle2D getBoundsInPoints() {
+ return Units.pixelToPoints(getHeader().getBoundsRectangle());
+ }
+
+ /**
* Return the image size in points
*
* @return the image size in points
*/
public Dimension2D getSize() {
- final Rectangle2D b = getHeader().getBoundsRectangle();
- return Units.pixelToPoints(new Dimension2DDouble(abs(b.getWidth()), abs(b.getHeight())));
+ final Rectangle2D b = getBoundsInPoints();
+ return new Dimension2DDouble(abs(b.getWidth()), abs(b.getHeight()));
}
private static double minX(Rectangle2D bounds) {
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
index ef5f4117d0..d1485c3975 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
@@ -93,12 +93,27 @@ public class HwmfGraphics {
}
}
- protected final List<HwmfDrawProperties> propStack = new LinkedList<>();
+ private static final Float[] WEIGHT_MAP = {
+ 900f, TextAttribute.WEIGHT_ULTRABOLD,
+ 800f, TextAttribute.WEIGHT_EXTRABOLD,
+ 750f, TextAttribute.WEIGHT_HEAVY,
+ 700f, TextAttribute.WEIGHT_BOLD,
+ 600f, TextAttribute.WEIGHT_DEMIBOLD,
+ 500f, TextAttribute.WEIGHT_MEDIUM,
+ 450f, TextAttribute.WEIGHT_SEMIBOLD,
+ 400f, TextAttribute.WEIGHT_REGULAR,
+ 300f, TextAttribute.WEIGHT_DEMILIGHT,
+ 200f, TextAttribute.WEIGHT_LIGHT,
+ 1f, TextAttribute.WEIGHT_EXTRA_LIGHT
+ };
+
+
+ private final List<HwmfDrawProperties> propStack = new LinkedList<>();
protected HwmfDrawProperties prop;
protected final Graphics2D graphicsCtx;
protected final BitSet objectIndexes = new BitSet();
protected final TreeMap<Integer,HwmfObjectTableEntry> objectTable = new TreeMap<>();
- protected final AffineTransform initialAT = new AffineTransform();
+ private final AffineTransform initialAT = new AffineTransform();
private static final Charset DEFAULT_CHARSET = LocaleUtil.CHARSET_1252;
@@ -207,9 +222,14 @@ public class HwmfGraphics {
case BS_DIBPATTERNPT: return getPatternPaint();
case BS_SOLID: return getSolidFill();
case BS_HATCHED: return getHatchedFill();
+ case BS_LINEAR_GRADIENT: return getLinearGradient();
}
}
+ protected Paint getLinearGradient() {
+ return null;
+ }
+
protected Paint getSolidFill() {
return getProperties().getBrushColor().getColor();
}
@@ -440,8 +460,13 @@ public class HwmfGraphics {
}
int trimLen;
- for (trimLen=0; trimLen<text.length-1; trimLen+=2) {
- if ((text[trimLen] == -1 && text[trimLen+1] == -1) ||
+ for (trimLen=0; trimLen<text.length; trimLen+=2) {
+ if (trimLen == text.length-1) {
+ if (text[trimLen] != 0) {
+ trimLen++;
+ }
+ break;
+ } else if ((text[trimLen] == -1 && text[trimLen+1] == -1) ||
((text[trimLen] & 0xE0) == 0 && text[trimLen+1] == 0)) {
break;
}
@@ -519,7 +544,7 @@ public class HwmfGraphics {
tx.translate(-pixelBounds.getWidth() / 2., 0);
break;
case RIGHT:
- tx.translate(-pixelBounds.getWidth(), 0);
+ tx.translate(-layout.getAdvance(), 0);
break;
}
@@ -581,7 +606,16 @@ public class HwmfGraphics {
if (font.isItalic()) {
as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
}
- as.addAttribute(TextAttribute.WEIGHT, font.getWeight());
+ // convert font weight to awt font weight - usually a font weight of 400 is regarded as regular
+ final int fw = font.getWeight();
+ Float awtFW = TextAttribute.WEIGHT_REGULAR;
+ for (int i=0; i<WEIGHT_MAP.length; i+=2) {
+ if (fw >= WEIGHT_MAP[i]) {
+ awtFW = WEIGHT_MAP[i+1];
+ break;
+ }
+ }
+ as.addAttribute(TextAttribute.WEIGHT, awtFW);
}
private double getFontHeight(HwmfFont font) {
@@ -661,7 +695,11 @@ public class HwmfGraphics {
// of the referenced image and can be also negative
Composite old = graphicsCtx.getComposite();
graphicsCtx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
- img.drawImage(graphicsCtx, normBounds, getSubImageInsets(srcBounds, img.getNativeBounds()));
+
+ boolean useDeviceBounds = (img instanceof HwmfImageRenderer);
+
+ img.drawImage(graphicsCtx, normBounds,
+ getSubImageInsets(srcBounds, useDeviceBounds ? img.getNativeBounds() : img.getBounds()));
graphicsCtx.setComposite(old);
graphicsCtx.setTransform(oldTrans);
@@ -683,9 +721,9 @@ public class HwmfGraphics {
// Todo: check if we need to normalize srcBounds x/y, in case of flipped images
// for now we assume the width/height is positive
int left = (int)Math.round((srcBounds.getX()-nativeBounds.getX())/nativeBounds.getWidth()*100_000.);
- int top = (int)Math.round((srcBounds.getY()-nativeBounds.getY())/nativeBounds.getWidth()*100_000.);
+ int top = (int)Math.round((srcBounds.getY()-nativeBounds.getY())/nativeBounds.getHeight()*100_000.);
int right = (int)Math.round((nativeBounds.getMaxX()-srcBounds.getMaxX())/nativeBounds.getWidth()*100_000.);
- int bottom = (int)Math.round((nativeBounds.getMaxY()-srcBounds.getMaxY())/nativeBounds.getWidth()*100_000.);
+ int bottom = (int)Math.round((nativeBounds.getMaxY()-srcBounds.getMaxY())/nativeBounds.getHeight()*100_000.);
return new Insets(top, left, bottom, right);
}
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphicsState.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphicsState.java
new file mode 100644
index 0000000000..e04046f4c2
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphicsState.java
@@ -0,0 +1,72 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hwmf.draw;
+
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.AffineTransform;
+
+import org.apache.poi.util.Internal;
+
+/**
+ * An util class for saving the state of a {@link java.awt.Graphics2D} object
+ */
+@Internal
+public class HwmfGraphicsState {
+ private Color background;
+ private Shape clip;
+ private Color color;
+ private Composite composite;
+ private Font font;
+ private Paint paint;
+ private Stroke stroke;
+ private AffineTransform trans;
+
+ /**
+ * Saves the state of the graphics2D object
+ */
+ public void backup(Graphics2D graphics2D) {
+ background = graphics2D.getBackground();
+ clip = graphics2D.getClip();
+ color = graphics2D.getColor();
+ composite = graphics2D.getComposite();
+ font = graphics2D.getFont();
+ paint = graphics2D.getPaint();
+ stroke = graphics2D.getStroke();
+ trans = graphics2D.getTransform();
+ }
+
+ /**
+ * Retrieves the state into the graphics2D object
+ */
+ public void restore(Graphics2D graphics2D) {
+ graphics2D.setBackground(background);
+ graphics2D.setClip(clip);
+ graphics2D.setColor(color);
+ graphics2D.setComposite(composite);
+ graphics2D.setFont(font);
+ graphics2D.setPaint(paint);
+ graphics2D.setStroke(stroke);
+ graphics2D.setTransform(trans);
+ }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java
index 7ac2af934b..68abb2432c 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java
@@ -21,7 +21,6 @@ import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
-import java.awt.Shape;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
@@ -114,20 +113,22 @@ public class HwmfImageRenderer implements ImageRenderer, EmbeddedExtractor {
return false;
}
+ HwmfGraphicsState graphicsState = new HwmfGraphicsState();
+ graphicsState.backup(graphics);
+
boolean isClipped = true;
if (clip == null) {
isClipped = false;
clip = new Insets(0,0,0,0);
}
- Shape clipOld = graphics.getClip();
if (isClipped) {
graphics.clip(anchor);
}
image.draw(graphics, getOuterBounds(anchor, clip));
- graphics.setClip(clipOld);
+ graphicsState.restore(graphics);
return true;
}
@@ -179,4 +180,9 @@ public class HwmfImageRenderer implements ImageRenderer, EmbeddedExtractor {
public Rectangle2D getNativeBounds() {
return image.getBounds();
}
+
+ @Override
+ public Rectangle2D getBounds() {
+ return Units.pointsToPixel(image == null ? new Rectangle2D.Double() : image.getBoundsInPoints());
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBrushStyle.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBrushStyle.java
index 903ba592ac..2d2c74577c 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBrushStyle.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBrushStyle.java
@@ -64,7 +64,11 @@ public enum HwmfBrushStyle {
/**
* Not supported
*/
- BS_MONOPATTERN(0x0009);
+ BS_MONOPATTERN(0x0009),
+ /**
+ * (POI arbitrary:) EMF/EMF+ specific value for linear gradient paint
+ */
+ BS_LINEAR_GRADIENT(0x0100);
int flag;
HwmfBrushStyle(int flag) {
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java
index 14432432db..076fe00f54 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java
@@ -288,7 +288,7 @@ public class HwmfFont implements FontInfo, GenericRecord {
* For all height comparisons, the font mapper SHOULD find the largest physical
* font that does not exceed the requested size.
*/
- protected int height;
+ protected double height;
/**
* A 16-bit signed integer that defines the average width, in logical units, of
@@ -433,7 +433,7 @@ public class HwmfFont implements FontInfo, GenericRecord {
facename = "SansSerif";
}
- public int getHeight() {
+ public double getHeight() {
return height;
}
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRegionMode.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRegionMode.java
index d6bc3fcb07..903cc39eee 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRegionMode.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRegionMode.java
@@ -45,7 +45,11 @@ public enum HwmfRegionMode {
/**
* The new clipping region is the current path (or the new region).
*/
- RGN_COPY(0x05, HwmfRegionMode::copyOp);
+ RGN_COPY(0x05, HwmfRegionMode::copyOp),
+ /**
+ * This is the opposite of {@link #RGN_DIFF}, and only made-up for compatibility with EMF+
+ */
+ RGN_COMPLEMENT(-1, HwmfRegionMode::complementOp);
private final int flag;
private final BiFunction<Shape,Shape,Shape> op;
@@ -125,4 +129,17 @@ public enum HwmfRegionMode {
private static Shape copyOp(final Shape oldClip, final Shape newClip) {
return (newClip == null || newClip.getBounds2D().isEmpty()) ? null : newClip;
}
+
+ private static Shape complementOp(final Shape oldClip, final Shape newClip) {
+ assert(newClip != null);
+ if (newClip.getBounds2D().isEmpty()) {
+ return oldClip;
+ } else if (oldClip == null) {
+ return newClip;
+ } else {
+ Area newArea = new Area(newClip);
+ newArea.subtract(new Area(oldClip));
+ return newArea;
+ }
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
index 533c85a821..f96e02199a 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
@@ -232,7 +232,10 @@ public class HwmfText {
@Override
public Map<String, Supplier<?>> getGenericProperties() {
- return GenericRecordUtil.getGenericProperties("text", () -> getText(StandardCharsets.US_ASCII));
+ return GenericRecordUtil.getGenericProperties(
+ "text", () -> getText(StandardCharsets.US_ASCII),
+ "reference", () -> reference
+ );
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java b/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java
index c5a4bad522..a33ea0ff1d 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java
@@ -36,6 +36,7 @@ import java.util.function.Supplier;
import org.apache.poi.common.usermodel.GenericRecord;
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
import org.apache.poi.hwmf.draw.HwmfGraphics;
+import org.apache.poi.hwmf.draw.HwmfGraphicsState;
import org.apache.poi.hwmf.record.HwmfHeader;
import org.apache.poi.hwmf.record.HwmfPlaceableHeader;
import org.apache.poi.hwmf.record.HwmfRecord;
@@ -127,8 +128,8 @@ public class HwmfPicture implements Iterable<HwmfRecord>, GenericRecord {
}
public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) {
- final Shape clip = ctx.getClip();
- final AffineTransform at = ctx.getTransform();
+ HwmfGraphicsState state = new HwmfGraphicsState();
+ state.backup(ctx);
try {
Rectangle2D wmfBounds = getBounds();
Rectangle2D innerBounds = getInnnerBounds();
@@ -137,13 +138,10 @@ public class HwmfPicture implements Iterable<HwmfRecord>, GenericRecord {
}
// scale output bounds to image bounds
- ctx.translate(graphicsBounds.getX(), graphicsBounds.getY());
- ctx.scale(graphicsBounds.getWidth()/wmfBounds.getWidth(), graphicsBounds.getHeight()/wmfBounds.getHeight());
+ ctx.translate(graphicsBounds.getCenterX(), graphicsBounds.getCenterY());
+ ctx.scale(graphicsBounds.getWidth()/innerBounds.getWidth(), graphicsBounds.getHeight()/innerBounds.getHeight());
+ ctx.translate(-innerBounds.getCenterX(), -innerBounds.getCenterY());
- ctx.translate(-wmfBounds.getX(), -wmfBounds.getY());
- ctx.translate(innerBounds.getCenterX(), innerBounds.getCenterY());
- ctx.scale(wmfBounds.getWidth()/innerBounds.getWidth(), wmfBounds.getHeight()/innerBounds.getHeight());
- ctx.translate(-wmfBounds.getCenterX(), -wmfBounds.getCenterY());
HwmfGraphics g = new HwmfGraphics(ctx, innerBounds);
HwmfDrawProperties prop = g.getProperties();
@@ -162,8 +160,7 @@ public class HwmfPicture implements Iterable<HwmfRecord>, GenericRecord {
idx++;
}
} finally {
- ctx.setTransform(at);
- ctx.setClip(clip);
+ state.restore(ctx);
}
}
@@ -214,19 +211,30 @@ public class HwmfPicture implements Iterable<HwmfRecord>, GenericRecord {
public HwmfHeader getHeader() {
return header;
}
-
+
/**
- * Return the image size in points
+ * Return the image bound in points
*
- * @return the image size in points
+ * @return the image bound in points
*/
- public Dimension2D getSize() {
+ public Rectangle2D getBoundsInPoints() {
double inch = (placeableHeader == null) ? 1440 : placeableHeader.getUnitsPerInch();
Rectangle2D bounds = getBounds();
-
+
//coefficient to translate from WMF dpi to 72dpi
double coeff = Units.POINT_DPI/inch;
- return new Dimension2DDouble(bounds.getWidth()*coeff, bounds.getHeight()*coeff);
+ return AffineTransform.getScaleInstance(coeff, coeff).createTransformedShape(bounds).getBounds2D();
+ }
+
+
+ /**
+ * Return the image size in points
+ *
+ * @return the image size in points
+ */
+ public Dimension2D getSize() {
+ Rectangle2D bounds = getBoundsInPoints();
+ return new Dimension2DDouble(bounds.getWidth(), bounds.getHeight());
}
public Iterable<HwmfEmbedded> getEmbeddings() {