You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2020/11/01 15:37:20 UTC

svn commit: r1883051 - in /poi: site/src/documentation/content/xdocs/components/slideshow/ trunk/src/java/org/apache/poi/common/usermodel/ trunk/src/java/org/apache/poi/sl/draw/ trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/ trunk/src/ooxml/java...

Author: kiwiwings
Date: Sun Nov  1 15:37:20 2020
New Revision: 1883051

URL: http://svn.apache.org/viewvc?rev=1883051&view=rev
Log:
#64716 - wmf display error
EMF: workaround for invalid EMF header bounds
EMF: add option to PPTX2PNG / DrawableHint to fallback to force EMF header bounds 
EMF: use RGB instead of HSL gradiants

Modified:
    poi/site/src/documentation/content/xdocs/components/slideshow/ppt-wmf-emf-renderer.xml
    poi/trunk/src/java/org/apache/poi/common/usermodel/PictureType.java
    poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
    poi/trunk/src/java/org/apache/poi/sl/draw/Drawable.java
    poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureLine.java
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java

Modified: poi/site/src/documentation/content/xdocs/components/slideshow/ppt-wmf-emf-renderer.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/components/slideshow/ppt-wmf-emf-renderer.xml?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/components/slideshow/ppt-wmf-emf-renderer.xml (original)
+++ poi/site/src/documentation/content/xdocs/components/slideshow/ppt-wmf-emf-renderer.xml Sun Nov  1 15:37:20 2020
@@ -56,6 +56,7 @@
                     -textAsShapes     text elements are saved as shapes in SVG, necessary for variable spacing
                                       often found in math formulas
                     -charset <cs>     sets the default charset to be used, defaults to Windows-1252
+                    -emfHeaderBounds  force the usage of the emf header bounds to calculate the bounding box
                 ]]>
             </source>
 

Modified: poi/trunk/src/java/org/apache/poi/common/usermodel/PictureType.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/common/usermodel/PictureType.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/common/usermodel/PictureType.java (original)
+++ poi/trunk/src/java/org/apache/poi/common/usermodel/PictureType.java Sun Nov  1 15:37:20 2020
@@ -17,6 +17,8 @@
 
 package org.apache.poi.common.usermodel;
 
+import org.apache.poi.poifs.filesystem.FileMagic;
+
 /**
  * General enum class to define a picture format/type
  *
@@ -65,4 +67,37 @@ public enum PictureType {
         this.contentType = contentType;
         this.extension = extension;
     }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public String getExtension() {
+        return extension;
+    }
+
+    public static PictureType valueOf(FileMagic fm) {
+        switch (fm) {
+            case BMP:
+                return PictureType.BMP;
+            case GIF:
+                return PictureType.GIF;
+            case JPEG:
+                return PictureType.JPEG;
+            case PNG:
+                return PictureType.PNG;
+            case XML:
+                // this is quite fuzzy, to suppose all XMLs are SVGs when handling pictures ...
+                return PictureType.SVG;
+            case WMF:
+                return PictureType.WMF;
+            case EMF:
+                return PictureType.EMF;
+            case TIFF:
+                return PictureType.TIFF;
+            default:
+            case UNKNOWN:
+                return PictureType.UNKNOWN;
+        }
+    }
 }

Modified: poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java Sun Nov  1 15:37:20 2020
@@ -27,6 +27,8 @@ import java.util.ServiceLoader;
 import java.util.function.Supplier;
 import java.util.stream.StreamSupport;
 
+import org.apache.poi.common.usermodel.PictureType;
+import org.apache.poi.poifs.filesystem.FileMagic;
 import org.apache.poi.sl.usermodel.PictureData;
 import org.apache.poi.sl.usermodel.PictureShape;
 import org.apache.poi.sl.usermodel.RectAlign;
@@ -36,11 +38,6 @@ import org.apache.poi.util.POILogger;
 
 public class DrawPictureShape extends DrawSimpleShape {
     private static final POILogger LOG = POILogFactory.getLogger(DrawPictureShape.class);
-    private static final String[] KNOWN_RENDERER = {
-        "org.apache.poi.hwmf.draw.HwmfImageRenderer",
-        "org.apache.poi.hemf.draw.HemfImageRenderer",
-        "org.apache.poi.xslf.draw.SVGImageRenderer"
-    };
 
     public DrawPictureShape(PictureShape<?,?> shape) {
         super(shape);
@@ -60,10 +57,14 @@ public class DrawPictureShape extends Dr
             }
 
             try {
-                String ct = data.getContentType();
+                byte[] dataBytes = data.getData();
+
+                PictureType type = PictureType.valueOf(FileMagic.valueOf(dataBytes));
+                String ct = (type == PictureType.UNKNOWN) ? data.getContentType() : type.getContentType();
+
                 ImageRenderer renderer = getImageRenderer(graphics, ct);
                 if (renderer.canRender(ct)) {
-                    renderer.loadImage(data.getData(), ct);
+                    renderer.loadImage(dataBytes, ct);
                     renderer.drawImage(graphics, anchor, insets);
                     return;
                 }
@@ -92,7 +93,7 @@ public class DrawPictureShape extends Dr
 
         // the fallback is the BitmapImageRenderer, at least it gracefully handles invalid images
         final Supplier<ImageRenderer> getFallback = () -> {
-            LOG.log(POILogger.WARN, "No suiteable image renderer found for content-type '"+
+            LOG.log(POILogger.WARN, "No suitable image renderer found for content-type '"+
             contentType+"' - include poi-scratchpad (for wmf/emf) or poi-ooxml (for svg) jars!");
             return fallback;
         };

Modified: poi/trunk/src/java/org/apache/poi/sl/draw/Drawable.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/draw/Drawable.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/draw/Drawable.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/draw/Drawable.java Sun Nov  1 15:37:20 2020
@@ -49,6 +49,7 @@ public interface Drawable {
             case 12: return "CURRENT_SLIDE";
             case 13: return "BUFFERED_IMAGE";
             case 14: return "DEFAULT_CHARSET";
+            case 15: return "EMF_FORCE_HEADER_BOUNDS";
             default: return "UNKNOWN_ID "+intKey();
             }
         }
@@ -153,6 +154,17 @@ public interface Drawable {
      */
     DrawableHint DEFAULT_CHARSET = new DrawableHint(14);
 
+    /**
+     * A boolean value to force the usage of the bounding box, which is specified in the EMF header.
+     * Defaults to {@code FALSE} - in this case the records are scanned for window and
+     * viewport records to determine the initial bounding box by using the following
+     * condition: {@code isValid(viewport) ? viewport : isValid(window) ? window : headerBounds }
+     * <p>
+     * This is a workaround switch, which might be removed in future releases, when the bounding box
+     * determination for the special cases is fixed.
+     * In most cases it's recommended to leave the default value.
+     */
+    DrawableHint EMF_FORCE_HEADER_BOUNDS = new DrawableHint(15);
 
     /**
      * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.

Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureLine.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureLine.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureLine.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureLine.java Sun Nov  1 15:37:20 2020
@@ -321,34 +321,11 @@ public abstract class SignatureLine {
      */
     protected byte[] plainPng() throws IOException {
         byte[] plain = getPlainSignature();
-        PictureType pictureType;
-        switch (FileMagic.valueOf(plain)) {
-            case PNG:
-                return plain;
-            case BMP:
-                pictureType = PictureType.BMP;
-                break;
-            case EMF:
-                pictureType = PictureType.EMF;
-                break;
-            case GIF:
-                pictureType = PictureType.GIF;
-                break;
-            case JPEG:
-                pictureType = PictureType.JPEG;
-                break;
-            case XML:
-                pictureType = PictureType.SVG;
-                break;
-            case TIFF:
-                pictureType = PictureType.TIFF;
-                break;
-            default:
-                throw new IllegalArgumentException("Unsupported picture format");
+        PictureType pictureType = PictureType.valueOf(FileMagic.valueOf(plain));
+        if (pictureType == PictureType.UNKNOWN) {
+            throw new IllegalArgumentException("Unsupported picture format");
         }
 
-
-
         ImageRenderer rnd = DrawPictureShape.getImageRenderer(null, pictureType.contentType);
         if (rnd == null) {
             throw new UnsupportedOperationException(pictureType + " can't be rendered - did you provide poi-scratchpad and its dependencies (batik et al.)");
@@ -375,11 +352,8 @@ public abstract class SignatureLine {
 
     /**
      * Generate the image for a signature line
-     * @param caption three lines separated by "\n" - usually something like "First name Last name\nRole\nname of the key"
-     * @param inputImage the plain signature - supported formats are PNG,GIF,JPEG,(SVG),EMF,WMF.
-     *                   for SVG,EMF,WMF poi-scratchpad needs to be in the class-/modulepath
-     *                   if {@code null}, the inputImage is not rendered
-     * @param invalidText for invalid signature images, use the given text
+     * @param showSignature show signature image - use {@code false} for placeholder images in to-be-signed documents
+     * @param showInvalidStamp print invalid stamp over the signature
      * @return the signature image in PNG format as byte array
      */
     protected byte[] generateImage(boolean showSignature, boolean showInvalidStamp) throws IOException {
@@ -462,28 +436,11 @@ public abstract class SignatureLine {
 
     private void determineContentType() {
         FileMagic fm = FileMagic.valueOf(plainSignature);
-        switch (fm) {
-            case GIF:
-                contentType = PictureType.GIF.contentType;
-                break;
-            case PNG:
-                contentType = PictureType.PNG.contentType;
-                break;
-            case JPEG:
-                contentType = PictureType.JPEG.contentType;
-                break;
-            case XML:
-                contentType = PictureType.SVG.contentType;
-                break;
-            case EMF:
-                contentType = PictureType.EMF.contentType;
-                break;
-            case WMF:
-                contentType = PictureType.WMF.contentType;
-                break;
-            default:
-                throw new IllegalArgumentException("unknown image type");
+        PictureType type = PictureType.valueOf(fm);
+        if (type == PictureType.UNKNOWN) {
+            throw new IllegalArgumentException("unknown image type");
         }
+        contentType = type.contentType;
     }
 
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java Sun Nov  1 15:37:20 2020
@@ -75,7 +75,8 @@ public final class PPTX2PNG {
             "                      some files (usually wmf) don't have a header, i.e. an identifiable file magic\n" +
             "    -textAsShapes     text elements are saved as shapes in SVG, necessary for variable spacing\n" +
             "                      often found in math formulas\n" +
-            "    -charset <cs>     sets the default charset to be used, defaults to Windows-1252";
+            "    -charset <cs>     sets the default charset to be used, defaults to Windows-1252\n" +
+            "    -emfHeaderBounds  force the usage of the emf header bounds to calculate the bounding box";
 
         System.out.println(msg);
         // no System.exit here, as we also run in junit tests!
@@ -104,6 +105,7 @@ public final class PPTX2PNG {
     private FileMagic defaultFileType = FileMagic.OLE2;
     private boolean textAsShapes = false;
     private Charset charset = LocaleUtil.CHARSET_1252;
+    private boolean emfHeaderBounds = false;
 
     private PPTX2PNG() {
     }
@@ -189,7 +191,9 @@ public final class PPTX2PNG {
                         charset = LocaleUtil.CHARSET_1252;
                     }
                     break;
-
+                case "-emfheaderbounds":
+                    emfHeaderBounds = true;
+                    break;
                 default:
                     file = new File(args[i]);
                     break;
@@ -279,6 +283,7 @@ public final class PPTX2PNG {
                     graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                     graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
                     graphics.setRenderingHint(Drawable.DEFAULT_CHARSET, getDefaultCharset());
+                    graphics.setRenderingHint(Drawable.EMF_FORCE_HEADER_BOUNDS, emfHeaderBounds);
 
                     graphics.scale(scale / lenSide, scale / lenSide);
 

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java Sun Nov  1 15:37:20 2020
@@ -101,6 +101,9 @@ public class HemfComment {
          */
         default void draw(HemfGraphics ctx) {}
 
+        default void calcBounds(Rectangle2D bounds, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) { }
+
+
         @Override
         default HemfCommentRecordType getGenericRecordType() {
             return getCommentRecordType();
@@ -132,6 +135,11 @@ public class HemfComment {
         }
 
         @Override
+        public void calcBounds(Rectangle2D window, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) {
+            data.calcBounds(window, viewport, renderState);
+        }
+
+        @Override
         public String toString() {
             return GenericRecordJsonWriter.marshal(this);
         }
@@ -333,6 +341,17 @@ public class HemfComment {
         }
 
         @Override
+        public void calcBounds(Rectangle2D window, Rectangle2D viewport, EmfRenderState[] renderState) {
+            renderState[0] = EmfRenderState.EMFPLUS_ONLY;
+            for (HemfPlusRecord r : records) {
+                r.calcBounds(window, viewport, renderState);
+                if (!window.isEmpty() && !viewport.isEmpty()) {
+                    break;
+                }
+            }
+        }
+
+        @Override
         public Map<String, Supplier<?>> getGenericProperties() {
             return null;
         }

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java Sun Nov  1 15:37:20 2020
@@ -18,6 +18,7 @@
 package org.apache.poi.hemf.record.emf;
 
 
+import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.util.Map;
 import java.util.function.Supplier;
@@ -56,6 +57,9 @@ public interface HemfRecord extends Gene
         }
     }
 
+    default void calcBounds(Rectangle2D window, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) {
+    }
+
     /**
      * Sets the header reference, in case the record needs to refer to it
      * @param header the emf header

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java Sun Nov  1 15:37:20 2020
@@ -22,6 +22,7 @@ import static org.apache.poi.hemf.record
 import static org.apache.poi.hwmf.record.HwmfDraw.normalizeBounds;
 
 import java.awt.geom.Dimension2D;
+import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.util.Map;
 import java.util.function.Supplier;
@@ -56,6 +57,13 @@ public class HemfWindowing {
         public HemfRecordType getGenericRecordType() {
             return getEmfRecordType();
         }
+
+        @Override
+        public void calcBounds(Rectangle2D window, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) {
+            double x = window.getX();
+            double y = window.getY();
+            window.setRect(x,y,size.getWidth(),size.getHeight());
+        }
     }
 
     /**
@@ -76,6 +84,13 @@ public class HemfWindowing {
         public HemfRecordType getGenericRecordType() {
             return getEmfRecordType();
         }
+
+        @Override
+        public void calcBounds(Rectangle2D window, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) {
+            double w = window.getWidth();
+            double h = window.getHeight();
+            window.setRect(origin.getX(),origin.getY(),w,h);
+        }
     }
 
     /**
@@ -96,6 +111,13 @@ public class HemfWindowing {
         public HemfRecordType getGenericRecordType() {
             return getEmfRecordType();
         }
+
+        @Override
+        public void calcBounds(Rectangle2D window, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) {
+            double x = viewport.getX();
+            double y = viewport.getY();
+            viewport.setRect(x,y,extents.getWidth(),extents.getHeight());
+        }
     }
 
     /**
@@ -116,6 +138,13 @@ public class HemfWindowing {
         public HemfRecordType getGenericRecordType() {
             return getEmfRecordType();
         }
+
+        @Override
+        public void calcBounds(Rectangle2D window, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) {
+            double w = viewport.getWidth();
+            double h = viewport.getHeight();
+            viewport.setRect(origin.getX(), origin.getY(), w, h);
+        }
     }
 
     /**
@@ -200,6 +229,16 @@ public class HemfWindowing {
         public HemfRecordType getGenericRecordType() {
             return getEmfRecordType();
         }
+
+        @Override
+        public void calcBounds(Rectangle2D window, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) {
+            double x = viewport.getX();
+            double y = viewport.getY();
+            double w = viewport.getWidth();
+            double h = viewport.getHeight();
+            viewport.setRect(x,y,w * scale.getWidth(),h * scale.getHeight());
+        }
+
     }
 
     /**
@@ -221,6 +260,15 @@ public class HemfWindowing {
         public HemfRecordType getGenericRecordType() {
             return getEmfRecordType();
         }
+
+        @Override
+        public void calcBounds(Rectangle2D window, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) {
+            double x = window.getX();
+            double y = window.getY();
+            double w = window.getWidth();
+            double h = window.getHeight();
+            window.setRect(x,y,w * scale.getWidth(),h * scale.getHeight());
+        }
     }
 
     /**

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java Sun Nov  1 15:37:20 2020
@@ -35,7 +35,6 @@ 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;
@@ -519,7 +518,7 @@ public class HemfPlusBrush {
     public static class EmfPlusLinearGradientBrushData implements EmfPlusBrushData {
         private int dataFlags;
         private EmfPlusWrapMode wrapMode;
-        private Rectangle2D rect = new Rectangle2D.Double();
+        private final Rectangle2D rect = new Rectangle2D.Double();
         private Color startColor, endColor;
         private AffineTransform blendTransform;
         private float[] positions;
@@ -529,8 +528,8 @@ public class HemfPlusBrush {
         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" };
+        private static final int[] FLAG_MASKS = {0x02, 0x04, 0x08, 0x10, 0x80};
+        private static final 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 {
@@ -543,7 +542,7 @@ public class HemfPlusBrush {
             // gradient is repeated.
             wrapMode = EmfPlusWrapMode.valueOf(leis.readInt());
 
-            int size = 2*LittleEndianConsts.INT_SIZE;
+            int size = 2 * LittleEndianConsts.INT_SIZE;
             size += readRectF(leis, rect);
 
             // An EmfPlusARGB object that specifies the color at the starting/ending boundary point of the linear gradient brush.
@@ -551,9 +550,9 @@ public class HemfPlusBrush {
             endColor = readARGB(leis.readInt());
 
             // skip reserved1/2 fields
-            leis.skipFully(2*LittleEndianConsts.INT_SIZE);
+            leis.skipFully(2 * LittleEndianConsts.INT_SIZE);
 
-            size += 4*LittleEndianConsts.INT_SIZE;
+            size += 4 * LittleEndianConsts.INT_SIZE;
 
             if (TRANSFORM.isSet(dataFlags)) {
                 size += readXForm(leis, (blendTransform = new AffineTransform()));
@@ -586,7 +585,7 @@ public class HemfPlusBrush {
             setColorProps(prop::setBrushColorsV, positionsV, this::getBlendVColorAt);
 
             if (!(isPreset() || isBlendH() || isBlendV())) {
-                prop.setBrushColorsH(Arrays.asList(kv(0f,startColor), kv(1f,endColor)));
+                prop.setBrushColorsH(Arrays.asList(kv(0f, startColor), kv(1f, endColor)));
             }
         }
 
@@ -607,7 +606,7 @@ public class HemfPlusBrush {
 
         @Override
         public Map<String, Supplier<?>> getGenericProperties() {
-            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            final Map<String, Supplier<?>> m = new LinkedHashMap<>();
             m.put("flags", GenericRecordUtil.getBitsAsString(() -> dataFlags, FLAG_MASKS, FLAG_NAMES));
             m.put("wrapMode", () -> wrapMode);
             m.put("rect", () -> rect);
@@ -635,24 +634,24 @@ public class HemfPlusBrush {
             return BLEND_FACTORS_V.isSet(dataFlags);
         }
 
-        private Map.Entry<Float,Color> getBlendColorAt(int index) {
+        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> 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 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) {
+        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) {
+                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 {
@@ -661,8 +660,26 @@ public class HemfPlusBrush {
         }
 
         private Color interpolateColors(final double factor) {
-            // https://stackoverflow.com/questions/1416560/hsl-interpolation
+            return interpolateColorsRGB(factor);
+        }
+
+        private Color interpolateColorsRGB(final double factor) {
+            // TODO: check IS_GAMMA_CORRECTED flag and maybe don't convert into scRGB
+            double[] start = DrawPaint.RGB2SCRGB(startColor);
+            double[] end = DrawPaint.RGB2SCRGB(endColor);
+
+            // compute the interpolated color in linear space
+            int a = (int)Math.round(startColor.getAlpha() + factor * (endColor.getAlpha() - startColor.getAlpha()));
+            double r = start[0] + factor * (end[0] - start[0]);
+            double g = start[1] + factor * (end[1] - start[1]);
+            double b = start[2] + factor * (end[2] - start[2]);
+
+            Color inter = DrawPaint.SCRGB2RGB(r,g,b);
+            return new Color(inter.getRed(), inter.getGreen(), inter.getBlue(), a);
+        }
 
+        /*
+        private Color interpolateColorsHSL(final double factor) {
             final double[] hslStart = DrawPaint.RGB2HSL(startColor);
             final double[] hslStop = DrawPaint.RGB2HSL(endColor);
 
@@ -673,16 +690,24 @@ public class HemfPlusBrush {
             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.;
+            // find closest match - decide if need to go clockwise or counter-clockwise
+            // https://stackoverflow.com/questions/1416560/hsl-interpolation
+            double hueMidCW = (hslStart[0]+hslStop[0])/2.;
+            double hueMidCCW = (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;
+            double hslDiff;
+            if (hueDelta.apply(hueMidCW) > hueDelta.apply(hueMidCCW)) {
+                hslDiff = (hslStart[0] < hslStop[0]) ? hslStop[0]-hslStart[0] : (360-hslStart[0])+hslStop[0];
+            } else {
+                hslDiff = (hslStart[0] < hslStop[0]) ? -hslStart[0]-(360-hslStop[0]) : -(hslStart[0]-hslStop[0]);
+            }
+            double hue = (hslStart[0]+hslDiff*factor)%360.;
 
             return DrawPaint.HSL2RGB(hue, sat, lum, alpha/255.);
-        }
+        } */
     }
 
     /** The EmfPlusPathGradientBrushData object specifies a path gradient for a graphics brush. */

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java Sun Nov  1 15:37:20 2020
@@ -20,6 +20,7 @@ package org.apache.poi.hemf.record.emfpl
 
 import static org.apache.poi.util.GenericRecordUtil.getEnumBitsAsString;
 
+import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.util.Map;
 import java.util.function.Supplier;
@@ -135,6 +136,11 @@ public class HemfPlusHeader implements H
     }
 
     @Override
+    public void calcBounds(Rectangle2D window, Rectangle2D viewport, EmfRenderState[] renderState) {
+        renderState[0] = EmfRenderState.EMF_DCONTEXT;
+    }
+
+    @Override
     public String toString() {
         return GenericRecordJsonWriter.marshal(this);
     }

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java Sun Nov  1 15:37:20 2020
@@ -163,6 +163,11 @@ public class HemfPlusMisc {
         public void draw(HemfGraphics ctx) {
             ctx.setRenderState(HemfGraphics.EmfRenderState.EMF_DCONTEXT);
         }
+
+        @Override
+        public void calcBounds(Rectangle2D window, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) {
+            renderState[0] = HemfGraphics.EmfRenderState.EMF_DCONTEXT;
+        }
     }
 
     /**

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java Sun Nov  1 15:37:20 2020
@@ -18,6 +18,7 @@
 package org.apache.poi.hemf.record.emfplus;
 
 
+import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 
 import org.apache.poi.common.usermodel.GenericRecord;
@@ -55,6 +56,9 @@ public interface HemfPlusRecord extends
     default void draw(HemfGraphics ctx) {
     }
 
+    default void calcBounds(Rectangle2D window, Rectangle2D viewport, HemfGraphics.EmfRenderState[] renderState) {
+    }
+
     @Override
     default HemfPlusRecordType getGenericRecordType() {
         return getEmfPlusRecordType();

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java?rev=1883051&r1=1883050&r2=1883051&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java Sun Nov  1 15:37:20 2020
@@ -19,6 +19,8 @@ package org.apache.poi.hemf.usermodel;
 
 
 import static java.lang.Math.abs;
+import static org.apache.poi.hemf.draw.HemfGraphics.EmfRenderState.EMFPLUS_ONLY;
+import static org.apache.poi.hemf.draw.HemfGraphics.EmfRenderState.EMF_ONLY;
 
 import java.awt.Graphics2D;
 import java.awt.Shape;
@@ -36,14 +38,14 @@ 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.emf.HemfComment;
 import org.apache.poi.hemf.record.emf.HemfHeader;
 import org.apache.poi.hemf.record.emf.HemfRecord;
 import org.apache.poi.hemf.record.emf.HemfRecordIterator;
-import org.apache.poi.hemf.record.emf.HemfWindowing;
 import org.apache.poi.hwmf.usermodel.HwmfCharsetAware;
 import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
+import org.apache.poi.sl.draw.Drawable;
 import org.apache.poi.util.Dimension2DDouble;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianInputStream;
@@ -114,26 +116,36 @@ public class HemfPicture implements Iter
      */
     public Rectangle2D getBounds() {
         Rectangle2D dim = getHeader().getFrameRectangle();
-        double x = dim.getX(), y = dim.getY();
-        double width = dim.getWidth(), height = dim.getHeight();
-        if (dim.isEmpty() || Math.rint(width) == 0 || Math.rint(height) == 0) {
-            for (HemfRecord r : getRecords()) {
-                if (r instanceof HemfWindowing.EmfSetWindowExtEx) {
-                    HemfWindowing.EmfSetWindowExtEx extEx = (HemfWindowing.EmfSetWindowExtEx)r;
-                    Dimension2D d = extEx.getSize();
-                    width = d.getWidth();
-                    height = d.getHeight();
-                    // keep searching - sometimes there's another record
-                }
-                if (r instanceof HemfWindowing.EmfSetWindowOrgEx) {
-                    HemfWindowing.EmfSetWindowOrgEx orgEx = (HemfWindowing.EmfSetWindowOrgEx)r;
-                    x = orgEx.getX();
-                    y = orgEx.getY();
-                }
+        boolean isInvalid = ReluctantRectangle2D.isEmpty(dim);
+        if (isInvalid) {
+            Rectangle2D lastDim = new ReluctantRectangle2D();
+            getInnerBounds(lastDim, new ReluctantRectangle2D());
+            if (!lastDim.isEmpty()) {
+                return lastDim;
             }
         }
+        return dim;
+    }
+
+    public void getInnerBounds(Rectangle2D window, Rectangle2D viewport) {
+        HemfGraphics.EmfRenderState[] renderState = { HemfGraphics.EmfRenderState.INITIAL };
+        for (HemfRecord r : getRecords()) {
+            if (
+                (renderState[0] == EMF_ONLY && r instanceof HemfComment.EmfComment) ||
+                (renderState[0] == EMFPLUS_ONLY && !(r instanceof HemfComment.EmfComment))
+            ) {
+                continue;
+            }
+
+            try {
+                r.calcBounds(window, viewport, renderState);
+            } catch (RuntimeException ignored) {
+            }
 
-        return new Rectangle2D.Double(x, y, width, height);
+            if (!window.isEmpty() && !viewport.isEmpty()) {
+                break;
+            }
+        }
     }
 
     /**
@@ -154,32 +166,36 @@ public class HemfPicture implements Iter
         final Rectangle2D b = getBoundsInPoints();
         return new Dimension2DDouble(abs(b.getWidth()), abs(b.getHeight()));
     }
-
-    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) {
         final Shape clip = ctx.getClip();
         final AffineTransform at = ctx.getTransform();
         try {
             Rectangle2D emfBounds = getHeader().getBoundsRectangle();
+            Rectangle2D winBounds = new ReluctantRectangle2D();
+            Rectangle2D viewBounds = new ReluctantRectangle2D();
+            getInnerBounds(winBounds, viewBounds);
+
+            Boolean forceHeader = (Boolean)ctx.getRenderingHint(Drawable.EMF_FORCE_HEADER_BOUNDS);
+            if (forceHeader == null) {
+                forceHeader = false;
+            }
+            // this is a compromise ... sometimes winBounds are totally off :(
+            // but mostly they fit better than the header bounds
+            Rectangle2D b =
+                !viewBounds.isEmpty() && !forceHeader
+                ? viewBounds
+                : !winBounds.isEmpty() && !forceHeader
+                ? winBounds
+                : emfBounds;
 
-            // scale output bounds to image bounds
             ctx.translate(graphicsBounds.getCenterX(), graphicsBounds.getCenterY());
-            ctx.scale(graphicsBounds.getWidth()/emfBounds.getWidth(), graphicsBounds.getHeight()/emfBounds.getHeight());
-            ctx.translate(-emfBounds.getCenterX(), -emfBounds.getCenterY());
+            ctx.scale(
+                graphicsBounds.getWidth()/b.getWidth(),
+                graphicsBounds.getHeight()/b.getHeight()
+            );
+            ctx.translate(-b.getCenterX(),-b.getCenterY());
 
-            HemfGraphics g = new HemfGraphics(ctx, emfBounds);
-            HemfDrawProperties prop = g.getProperties();
-            prop.setWindowOrg(emfBounds.getX(), emfBounds.getY());
-            prop.setWindowExt(emfBounds.getWidth(), emfBounds.getHeight());
-            prop.setViewportOrg(emfBounds.getX(), emfBounds.getY());
-            prop.setViewportExt(emfBounds.getWidth(), emfBounds.getHeight());
+            HemfGraphics g = new HemfGraphics(ctx, b);
 
             for (HemfRecord r : getRecords()) {
                 try {
@@ -214,4 +230,43 @@ public class HemfPicture implements Iter
     public Charset getDefaultCharset() {
         return defaultCharset;
     }
+
+
+    private static class ReluctantRectangle2D extends Rectangle2D.Double {
+        private boolean offsetSet = false;
+        private boolean rangeSet = false;
+
+        public ReluctantRectangle2D() {
+            super(-1,-1,0,0);
+        }
+
+        @Override
+        public void setRect(double x, double y, double w, double h) {
+            if (offsetSet && rangeSet) {
+                return;
+            }
+            super.setRect(
+                offsetSet ? this.x : x,
+                offsetSet ? this.y : y,
+                rangeSet ? this.width : w,
+                rangeSet ? this.height : h);
+            offsetSet |= (x != -1 || y != -1);
+            rangeSet |= (w != 0 || h != 0);
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return isEmpty(this);
+        }
+
+        public static boolean isEmpty(Rectangle2D r) {
+            double w = Math.rint(r.getWidth());
+            double h = Math.rint(r.getHeight());
+            return
+                (w <= 0.0) || (h <= 0.0) ||
+                (r.getX() == -1 && r.getY() == -1) ||
+                // invalid emf bound have sometimes 1,1 as dimension
+                (w == 1 && h == 1);
+        }
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org