From 4a38d5dad4bbb90d51fa5cf7f36487d97646087c Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Fri, 23 Nov 2018 02:08:28 +0000 Subject: [PATCH] #60656 - Support export file that contains emf and render it correctly git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1847209 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/hemf/record/emf/HemfMisc.java | 41 ++++++++++++++++++- .../poi/hemf/record/emf/HemfRecord.java | 6 +++ .../poi/hemf/usermodel/HemfPicture.java | 28 ++++++++++--- .../apache/poi/hwmf/draw/HwmfGraphics.java | 10 ----- 4 files changed, 68 insertions(+), 17 deletions(-) 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 55d8c4cc23..5e04e932d6 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 @@ -23,7 +23,9 @@ import static org.apache.poi.hemf.record.emf.HemfFill.readXForm; import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE; import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; @@ -31,6 +33,7 @@ import java.util.Arrays; import java.util.List; import java.util.function.Function; +import org.apache.poi.hemf.draw.HemfDrawProperties; import org.apache.poi.hemf.draw.HemfGraphics; import org.apache.poi.hwmf.draw.HwmfDrawProperties; import org.apache.poi.hwmf.draw.HwmfGraphics; @@ -632,6 +635,7 @@ public class HemfMisc { public static class EmfModifyWorldTransform implements HemfRecord { protected final AffineTransform xForm = new AffineTransform(); protected HemfModifyWorldTransformMode modifyWorldTransformMode; + protected HemfHeader header; @Override public HemfRecordType getEmfRecordType() { @@ -652,17 +656,50 @@ public class HemfMisc { return size + LittleEndianConsts.INT_SIZE; } + @Override + public void setHeader(HemfHeader header) { + this.header = header; + } + @Override public void draw(HemfGraphics ctx) { if (modifyWorldTransformMode == null) { return; } + final HemfDrawProperties prop = ctx.getProperties(); + final AffineTransform tx; switch (modifyWorldTransformMode) { case MWT_LEFTMULTIPLY: + + AffineTransform wsTrans; + final Rectangle2D win = prop.getWindow(); + boolean noSetWindowExYet = win.getWidth() == 1 && win.getHeight() == 1; + if (noSetWindowExYet) { + // TODO: understand world-space transformation [MSDN-WRLDPGSPC] + // experimental and horrible solved, because the world-space transformation behind it + // is not understood :( + // only found one example which had landscape bounds and transform of 90 degress + + try { + wsTrans = xForm.createInverse(); + } catch (NoninvertibleTransformException e) { + wsTrans = new AffineTransform(); + } + + Rectangle2D emfBounds = header.getBoundsRectangle(); + + if (xForm.getShearX() == -1.0 && xForm.getShearY() == 1.0) { + // rotate 90 deg + wsTrans.translate(-emfBounds.getHeight(), emfBounds.getHeight()); + } + } else { + wsTrans = adaptXForm(ctx.getTransform()); + } + tx = ctx.getTransform(); - tx.concatenate(adaptXForm(tx)); + tx.concatenate(wsTrans); break; case MWT_RIGHTMULTIPLY: tx = ctx.getTransform(); @@ -690,7 +727,7 @@ public class HemfMisc { Function nn = (d) -> (d == 0. ? 0. : d); double yDiff = Math.signum(nn.apply(xForm.getTranslateY())) == Math.signum(nn.apply(other.getTranslateY())) ? 1. : -1.; double xDiff = Math.signum(nn.apply(xForm.getTranslateX())) == Math.signum(nn.apply(other.getTranslateX())) ? 1. : -1.; - return new AffineTransform( + return new AffineTransform( xForm.getScaleX() == 0 ? 1. : xForm.getScaleX(), yDiff * xForm.getShearY(), xDiff * xForm.getShearX(), diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java index 4fb2cc22d4..41e9f30442 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java @@ -52,4 +52,10 @@ public interface HemfRecord { ((HwmfRecord) this).draw(ctx); } } + + /** + * Sets the header reference, in case the record needs to refer to it + * @param header the emf header + */ + default void setHeader(HemfHeader header) {} } 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 8d49fdd0e1..6d53ae23ea 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java +++ b/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java @@ -67,7 +67,14 @@ public class HemfPicture implements Iterable { // in case the (first) parsing throws an exception, we can provide the // records up to that point isParsed = true; - new HemfRecordIterator(stream).forEachRemaining(records::add); + HemfHeader[] header = new HemfHeader[1]; + new HemfRecordIterator(stream).forEachRemaining(r -> { + if (r instanceof HemfHeader) { + header[0] = (HemfHeader) r; + } + r.setHeader(header[0]); + records.add(r); + }); } return records; } @@ -116,23 +123,34 @@ public class HemfPicture implements Iterable { return new Dimension2DDouble(Math.abs(width*coeff), Math.abs(height*coeff)); } + private static double minX(Rectangle2D bounds) { + return Math.min(bounds.getMinX(), bounds.getMaxX()); + } + + private static double minY(Rectangle2D bounds) { + return Math.min(bounds.getMinY(), bounds.getMaxY()); + } + public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) { HemfHeader header = (HemfHeader)getRecords().get(0); AffineTransform at = ctx.getTransform(); try { Rectangle2D emfBounds = header.getBoundsRectangle(); - ctx.translate(graphicsBounds.getCenterX()-emfBounds.getCenterX(), graphicsBounds.getCenterY()-emfBounds.getCenterY()); // scale output bounds to image bounds - ctx.translate(emfBounds.getCenterX(), emfBounds.getCenterY()); + ctx.translate(minX(graphicsBounds), minY(graphicsBounds)); ctx.scale(graphicsBounds.getWidth()/emfBounds.getWidth(), graphicsBounds.getHeight()/emfBounds.getHeight()); - ctx.translate(-emfBounds.getCenterX(), -emfBounds.getCenterY()); + ctx.translate(-minX(emfBounds), -minY(emfBounds)); int idx = 0; HemfGraphics g = new HemfGraphics(ctx, emfBounds); for (HemfRecord r : getRecords()) { - g.draw(r); + try { + g.draw(r); + } catch (RuntimeException ignored) { + + } idx++; } } finally { 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 dd189a0f55..2f9811fc63 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java @@ -374,9 +374,6 @@ public class HwmfGraphics { final HwmfDrawProperties prop = getProperties(); final AffineTransform at = graphicsCtx.getTransform(); - if (at.getScaleX() == 0. || at.getScaleY() == 0.) { - return; - } try { at.createInverse(); @@ -687,11 +684,4 @@ public class HwmfGraphics { graphicsCtx.setTransform(at); } } - - /** - * @return the bounding box - */ - public Rectangle2D getBbox() { - return (Rectangle2D)bbox.clone(); - } } -- 2.39.5