]> source.dussan.org Git - poi.git/commitdiff
#64693 - POI HwmfGraphics cannot read the embedded document title
authorAndreas Beeker <kiwiwings@apache.org>
Sun, 30 Aug 2020 11:18:21 +0000 (11:18 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Sun, 30 Aug 2020 11:18:21 +0000 (11:18 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1881322 13f79535-47bb-0310-9956-ffa450edef68

23 files changed:
src/java/org/apache/poi/sl/draw/Drawable.java
src/java/org/apache/poi/sl/draw/ImageRenderer.java
src/java/org/apache/poi/util/GenericRecordJsonWriter.java
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
src/ooxml/java/org/apache/poi/xslf/util/EMFHandler.java
src/ooxml/java/org/apache/poi/xslf/util/MFProxy.java
src/ooxml/java/org/apache/poi/xslf/util/PPTHandler.java
src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java
src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
src/resources/devtools/forbidden-signatures.txt
src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java
src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java
src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfCharsetAware.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java
src/testcases/org/apache/poi/poifs/filesystem/TestFileMagic.java
test-data/slideshow/bug64693.pptx [new file with mode: 0644]

index fd0ea1b6faa0be4107bca2ae0c0ca87e823b8d33..919e1a6f25780c84380e4c310b7ecc6b8598bfdf 100644 (file)
@@ -28,11 +28,11 @@ public interface Drawable {
         protected DrawableHint(int id) {
             super(id);
         }
-        
+
         public boolean isCompatibleValue(Object val) {
             return true;
         }
-        
+
         public String toString() {
             switch (intKey()) {
             case 1: return "DRAW_FACTORY";
@@ -48,11 +48,12 @@ public interface Drawable {
             case 11: return "GRESTORE";
             case 12: return "CURRENT_SLIDE";
             case 13: return "BUFFERED_IMAGE";
+            case 14: return "DEFAULT_CHARSET";
             default: return "UNKNOWN_ID "+intKey();
             }
         }
     }
-    
+
     /**
      * {@link DrawFactory} which will be used to draw objects into this graphics context
      */
@@ -96,7 +97,7 @@ public interface Drawable {
      * Internal key for caching the preset geometries
      */
     DrawableHint PRESET_GEOMETRY_CACHE = new DrawableHint(6);
-    
+
     /**
      * draw text via {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)}
      */
@@ -110,11 +111,11 @@ public interface Drawable {
     /**
      * Use this object to resolve unknown / missing fonts when rendering slides.
      * The font handler must be of type {@link DrawFontManager}.<p>
-     * 
-     * In case a {@code FONT_HANDLER} is register, {@code FONT_FALLBACK} and {@code FONT_MAP} are ignored 
+     *
+     * In case a {@code FONT_HANDLER} is register, {@code FONT_FALLBACK} and {@code FONT_MAP} are ignored
      */
     DrawableHint FONT_HANDLER = new DrawableHint(7);
-    
+
     /**
      * Key for a font fallback map of type {@code Map<String,String>} which maps
      * the original font family (key) to the fallback font family (value).
@@ -128,10 +129,10 @@ public interface Drawable {
      * the original font family (key) to the mapped font family (value)
      */
     DrawableHint FONT_MAP = new DrawableHint(9);
-    
+
     DrawableHint GSAVE = new DrawableHint(10);
     DrawableHint GRESTORE = new DrawableHint(11);
-    
+
     /**
      * The Common SL Draw API works sometimes cascading, but there are places
      * where the current slide context need to be evaluated, e.g. when slide numbers
@@ -145,24 +146,32 @@ public interface Drawable {
      */
     DrawableHint BUFFERED_IMAGE = new DrawableHint(13);
 
+    /**
+     * Sets the default charset to render text elements.
+     * Opposed to other windows libraries in POI this simply defaults to Windows-1252.
+     * The rendering value is of type {@link java.nio.charset.Charset}
+     */
+    DrawableHint DEFAULT_CHARSET = new DrawableHint(14);
+
+
     /**
      * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
      *
      * @param graphics the graphics whos transform matrix will be modified
      */
     void applyTransform(Graphics2D graphics);
-    
+
     /**
      * Draw this shape into the supplied canvas
      *
      * @param graphics the graphics to draw into
      */
     void draw(Graphics2D graphics);
-    
+
     /**
      * draw any content within this shape (image, text, etc.).
      *
      * @param graphics the graphics to draw into
      */
-    void drawContent(Graphics2D graphics);    
+    void drawContent(Graphics2D graphics);
 }
index 5051d4593b483ff9a5ba69a154a81365e771e059..6f255c72d06c4723590864ae6b35d6fb5db82d9e 100644 (file)
@@ -25,6 +25,7 @@ import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 
 import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.util.Dimension2DDouble;
@@ -154,4 +155,11 @@ public interface ImageRenderer {
 
     default GenericRecord getGenericRecord() { return null; }
 
+    /**
+     * Sets the default charset to render text elements.
+     * Opposed to other windows libraries in POI this simply defaults to Windows-1252.
+     *
+     * @param defaultCharset the default charset
+     */
+    default void setDefaultCharset(Charset defaultCharset) {}
 }
\ No newline at end of file
index 32a43c3a5ea785d44ed9a7eb21b2625823f87ea2..c46a535a0b077e8c606e9a40ef4450812438f68f 100644 (file)
@@ -26,6 +26,12 @@ import java.awt.geom.Path2D;
 import java.awt.geom.PathIterator;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DirectColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.PackedColorModel;
 import java.io.Closeable;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -58,6 +64,7 @@ public class GenericRecordJsonWriter implements Closeable {
     private static final Pattern ESC_CHARS = Pattern.compile("[\"\\p{Cntrl}\\\\]");
     private static final String NL = System.getProperty("line.separator");
 
+
     @FunctionalInterface
     protected interface GenericRecordHandler {
         /**
@@ -92,6 +99,7 @@ public class GenericRecordJsonWriter implements Closeable {
         handler(Path2D.class, GenericRecordJsonWriter::printPath);
         handler(AffineTransform.class, GenericRecordJsonWriter::printAffineTransform);
         handler(Color.class, GenericRecordJsonWriter::printColor);
+        handler(BufferedImage.class, GenericRecordJsonWriter::printImage);
         handler(Array.class, GenericRecordJsonWriter::printArray);
         handler(Object.class, GenericRecordJsonWriter::printObject);
     }
@@ -483,6 +491,40 @@ public class GenericRecordJsonWriter implements Closeable {
         return true;
     }
 
+    protected boolean printImage(String name, Object o) {
+        BufferedImage img = (BufferedImage)o;
+
+        final String[] COLOR_SPACES = {
+            "XYZ","Lab","Luv","YCbCr","Yxy","RGB","GRAY","HSV","HLS","CMYK","Unknown","CMY","Unknown"
+        };
+
+        final String[] IMAGE_TYPES = {
+                "CUSTOM","INT_RGB","INT_ARGB","INT_ARGB_PRE","INT_BGR","3BYTE_BGR","4BYTE_ABGR","4BYTE_ABGR_PRE",
+                "USHORT_565_RGB","USHORT_555_RGB","BYTE_GRAY","USHORT_GRAY","BYTE_BINARY","BYTE_INDEXED"
+        };
+
+        printName(name);
+        ColorModel cm = img.getColorModel();
+        String colorType =
+            (cm instanceof IndexColorModel) ? "indexed" :
+            (cm instanceof ComponentColorModel) ? "component" :
+            (cm instanceof DirectColorModel) ? "direct" :
+            (cm instanceof PackedColorModel) ? "packed" : "unknown";
+        fw.write(
+            "{ \"width\": "+img.getWidth()+
+            ", \"height\": "+img.getHeight()+
+            ", \"type\": \""+IMAGE_TYPES[img.getType()]+"\""+
+            ", \"colormodel\": \""+colorType+"\""+
+            ", \"pixelBits\": "+cm.getPixelSize()+
+            ", \"numComponents\": "+cm.getNumComponents()+
+            ", \"colorSpace\": \""+COLOR_SPACES[Math.min(cm.getColorSpace().getType(),12)]+"\""+
+            ", \"transparency\": "+cm.getTransparency()+
+            ", \"alpha\": "+cm.hasAlpha()+
+            "}"
+        );
+        return true;
+    }
+
     static String trimHex(final long l, final int size) {
         final String b = Long.toHexString(l);
         int len = b.length();
index 1c3890a1599d27ad1a7f8932769adbc2f828f1c2..b57e2da602f59faaf0a318766c5f3d7dde5eb6f9 100644 (file)
@@ -738,7 +738,9 @@ public class XSLFTextRun implements TextRun {
                 // SYMBOL is missing
 
                 if (font == null || !font.isSetTypeface() || "".equals(font.getTypeface())) {
-                    font = coll.getLatin();
+                    // don't fallback to latin but bubble up in the style hierarchy (slide -> layout -> master -> theme)
+                    return null;
+//                    font = coll.getLatin();
                 }
             }
 
index 9d7e620bb4819738dab0c2299231618051cf0868..761c8196128378359c7b3a24dc28655f4fd36af4 100644 (file)
@@ -25,6 +25,7 @@ import java.awt.geom.Rectangle2D;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.Collections;
 
 import org.apache.poi.common.usermodel.GenericRecord;
@@ -111,4 +112,9 @@ class EMFHandler extends MFProxy {
             ? ((EmbeddedExtractor) imgr).getEmbeddings()
             : Collections.emptyList();
     }
+
+    @Override
+    void setDefaultCharset(Charset charset) {
+        imgr.setDefaultCharset(charset);
+    }
 }
index 435fdb3e78909ec4bd8f238e6c228446414ea213..5a02c6369588fe3cee1d0a755ae19f4eb3d8fa16 100644 (file)
@@ -25,6 +25,7 @@ import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.Collections;
 import java.util.Set;
 
@@ -64,4 +65,6 @@ abstract class MFProxy implements Closeable {
     abstract GenericRecord getRoot();
 
     abstract Iterable<EmbeddedPart> getEmbeddings(int slideNo);
+
+    abstract void setDefaultCharset(Charset charset);
 }
index debb34f005ed07c800da60f9c94ccf749d0f3a4b..79d676ddc076e86d63650f5cc12edcd89250bb1d 100644 (file)
@@ -27,6 +27,7 @@ import java.awt.geom.Dimension2D;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
@@ -49,12 +50,14 @@ import org.apache.poi.sl.usermodel.SlideShow;
 import org.apache.poi.sl.usermodel.SlideShowFactory;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.Internal;
+import org.apache.poi.util.LocaleUtil;
 
 /** Handler for ppt and pptx files */
 @Internal
 class PPTHandler extends MFProxy {
     private SlideShow<?,?> ppt;
     private Slide<?,?> slide;
+    private Charset defaultCharset = LocaleUtil.CHARSET_1252;
 
     @Override
     public void parse(File file) throws IOException {
@@ -67,6 +70,9 @@ class PPTHandler extends MFProxy {
                 throw e;
             }
         }
+        if (ppt == null) {
+            throw new IOException("Unknown file format or missing poi-scratchpad.jar / poi-ooxml.jar");
+        }
         slide = ppt.getSlides().get(0);
     }
 
@@ -81,6 +87,9 @@ class PPTHandler extends MFProxy {
                 throw e;
             }
         }
+        if (ppt == null) {
+            throw new IOException("Unknown file format or missing poi-scratchpad.jar / poi-ooxml.jar");
+        }
         slide = ppt.getSlides().get(0);
     }
 
@@ -162,8 +171,8 @@ class PPTHandler extends MFProxy {
         ;
     }
 
-    private static EmbeddedPart fromObjectShape(Shape s) {
-        final ObjectShape os = (ObjectShape)s;
+    private static EmbeddedPart fromObjectShape(Shape<?,?> s) {
+        final ObjectShape<?,?> os = (ObjectShape<?,?>)s;
         final ObjectData od = os.getObjectData();
         EmbeddedPart embed = new EmbeddedPart();
         embed.setName(od.getFileName());
@@ -177,4 +186,8 @@ class PPTHandler extends MFProxy {
         });
         return embed;
     }
+
+    @Override
+    void setDefaultCharset(Charset charset) {
+    }
 }
index e3bbf62001889fdf0234dd0b2a8aa9e0af169c87..384255c57afaa6b3c4ba2007bb4d3b6939bf7140 100644 (file)
@@ -27,15 +27,18 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.Locale;
 import java.util.Set;
 import java.util.regex.Pattern;
 
 import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.poifs.filesystem.FileMagic;
+import org.apache.poi.sl.draw.Drawable;
 import org.apache.poi.sl.draw.EmbeddedExtractor.EmbeddedPart;
 import org.apache.poi.util.Dimension2DDouble;
 import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.LocaleUtil;
 import org.apache.poi.xslf.util.OutputFormat.BitmapFormat;
 import org.apache.poi.xslf.util.OutputFormat.SVGFormat;
 
@@ -71,7 +74,8 @@ public final class PPTX2PNG {
             "    -inputType <type> default input file type (OLE2,WMF,EMF), default is OLE2 = Powerpoint\n" +
             "                      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";
+            "                      often found in math formulas\n" +
+            "    -charset          sets the default charset to be used, defaults to Windows-1252";
 
         System.out.println(msg);
         // no System.exit here, as we also run in junit tests!
@@ -99,6 +103,7 @@ public final class PPTX2PNG {
     private boolean extractEmbedded = false;
     private FileMagic defaultFileType = FileMagic.OLE2;
     private boolean textAsShapes = false;
+    private Charset charset = LocaleUtil.CHARSET_1252;
 
     private PPTX2PNG() {
     }
@@ -176,6 +181,15 @@ public final class PPTX2PNG {
                 case "-extractEmbedded":
                     extractEmbedded = true;
                     break;
+                case "-charset":
+                    if (opt != null) {
+                        charset = Charset.forName(opt);
+                        i++;
+                    } else {
+                        charset = LocaleUtil.CHARSET_1252;
+                    }
+                    break;
+
                 default:
                     file = new File(args[i]);
                     break;
@@ -264,6 +278,7 @@ public final class PPTX2PNG {
                     graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
                     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.scale(scale / lenSide, scale / lenSide);
 
@@ -315,7 +330,7 @@ public final class PPTX2PNG {
     }
 
     private void dumpRecords(MFProxy proxy) throws IOException {
-        if (dumpfile == null) {
+        if (dumpfile == null || "null".equals(dumpfile.getPath())) {
             return;
         }
         GenericRecord gr = proxy.getRoot();
@@ -387,6 +402,7 @@ public final class PPTX2PNG {
             proxy.setQuite(quiet);
             proxy.parse(file);
         }
+        proxy.setDefaultCharset(charset);
 
         return proxy;
     }
@@ -400,6 +416,10 @@ public final class PPTX2PNG {
         return INPUT_PATTERN.matcher(inname).replaceAll(outpat);
     }
 
+    private Charset getDefaultCharset() {
+        return charset;
+    }
+
     static class NoScratchpadException extends IOException {
         NoScratchpadException() {
         }
index 96d30601e5a6d55dd2485c86ec2c97fa51646e6b..df23f14f28529c9ec33cc63b3f97d95c4a1a4c82 100644 (file)
 
 package org.apache.poi.xslf.usermodel;
 
+import static java.util.Arrays.asList;
 import static org.junit.Assume.assumeFalse;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -39,13 +42,14 @@ import org.junit.runners.Parameterized.Parameters;
 /**
  * Test class for testing PPTX2PNG utility which renders .ppt and .pptx slideshows
  */
+@SuppressWarnings("ConstantConditions")
 @RunWith(Parameterized.class)
 public class TestPPTX2PNG {
     private static boolean xslfOnly;
     private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance();
     private static final File basedir = null;
     private static final String files =
-        "53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, " +
+        "bug64693.pptx, 53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, " +
         "backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx," +
         "customGeo.pptx, customGeo.ppt, wrench.emf, santa.wmf, missing-moveto.ppt";
 
@@ -62,7 +66,6 @@ public class TestPPTX2PNG {
     @Parameter
     public String pptFile;
 
-    @SuppressWarnings("ConstantConditions")
     @Parameters(name="{0}")
     public static Collection<String> data() {
         Function<String, Stream<String>> fun = (basedir == null) ? Stream::of :
@@ -75,7 +78,9 @@ public class TestPPTX2PNG {
     public void render() throws Exception {
         assumeFalse("ignore HSLF (.ppt) / HEMF (.emf) / HWMF (.wmf) files in no-scratchpad run", xslfOnly && pptFile.matches(".*\\.(ppt|emf|wmf)$"));
 
-        String[] args = {
+        // bug64693.pptx
+
+        final List<String> args = new ArrayList<>(asList(
             "-format", "null", // png,gif,jpg,svg or null for test
             "-slide", "-1", // -1 for all
             "-outdir", new File("build/tmp/").getCanonicalPath(),
@@ -84,10 +89,17 @@ public class TestPPTX2PNG {
             "-dump", "null",
             "-quiet",
             "-fixside", "long",
-            "-scale", "800",
-            // "-scale", "1.333333333",
-            (basedir == null ? samples.getFile(pptFile) : new File(basedir, pptFile)).getAbsolutePath()
-        };
-        PPTX2PNG.main(args);
+            "-scale", "800"
+        ));
+
+        if ("bug64693.pptx".equals(pptFile)) {
+            args.addAll(asList(
+                "-charset", "GBK"
+            ));
+        }
+
+        args.add((basedir == null ? samples.getFile(pptFile) : new File(basedir, pptFile)).getAbsolutePath());
+
+        PPTX2PNG.main(args.toArray(new String[0]));
     }
 }
index dbe56c1022d9998c0c6ed2ca76566d04bb01bff2..0ed4e2521b157eede1375e45107ba664ab4262a9 100644 (file)
@@ -139,4 +139,7 @@ java.lang.String#toString()
 javax.xml.bind.DatatypeConverter\r
 \r
 @defaultMessage don't rely on the threads ContextClassLoader - provide the classloader via load(Class, Classloader)\r
-java.util.ServiceLoader#load(java.lang.Class)
\ No newline at end of file
+java.util.ServiceLoader#load(java.lang.Class)\r
+\r
+@defaultMessage use java.nio.charset.StandardCharsets instead\r
+org.apache.commons.codec.Charsets
\ No newline at end of file
index 42cef834a93f08b50067c948fd1c25e9582bbcf8..95d3b7e70a011b6b1b322530ec521d57777035fa 100644 (file)
@@ -28,12 +28,14 @@ import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 
 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.Drawable;
 import org.apache.poi.sl.draw.EmbeddedExtractor;
 import org.apache.poi.sl.draw.ImageRenderer;
 import org.apache.poi.sl.usermodel.PictureData;
@@ -43,6 +45,7 @@ import org.apache.poi.util.Units;
 public class HemfImageRenderer implements ImageRenderer, EmbeddedExtractor {
     HemfPicture image;
     double alpha;
+    boolean charsetInitialized = false;
 
     @Override
     public boolean canRender(String contentType) {
@@ -104,6 +107,11 @@ public class HemfImageRenderer implements ImageRenderer, EmbeddedExtractor {
             return false;
         }
 
+        Charset cs = (Charset)graphics.getRenderingHint(Drawable.DEFAULT_CHARSET);
+        if (cs != null && !charsetInitialized) {
+            setDefaultCharset(cs);
+        }
+
         HwmfGraphicsState graphicsState = new HwmfGraphicsState();
         graphicsState.backup(graphics);
 
@@ -141,4 +149,10 @@ public class HemfImageRenderer implements ImageRenderer, EmbeddedExtractor {
     public Rectangle2D getBounds() {
         return Units.pointsToPixel(image == null ? new Rectangle2D.Double() : image.getBoundsInPoints());
     }
+
+    @Override
+    public void setDefaultCharset(Charset defaultCharset) {
+        image.setDefaultCharset(defaultCharset);
+        charsetInitialized = true;
+    }
 }
index 4b1060b7bd7cace675d9e83cd6ef0d5b39fe8027..0d055d340f83815ed55a8b98f811970ccb9ddf22 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.poi.hemf.record.emf;
 
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -33,6 +34,7 @@ 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.HwmfCharsetAware;
 import org.apache.poi.hwmf.usermodel.HwmfPicture;
 import org.apache.poi.util.GenericRecordJsonWriter;
 import org.apache.poi.util.GenericRecordUtil;
@@ -105,7 +107,7 @@ public class HemfComment {
         }
     }
 
-    public static class EmfComment implements HemfRecord {
+    public static class EmfComment implements HemfRecord, HwmfCharsetAware {
         private EmfCommentData data;
 
         @Override
@@ -146,6 +148,13 @@ public class HemfComment {
             }
             assert(commentIdentifier == commentType.id);
         }
+
+        @Override
+        public void setCharsetProvider(Supplier<Charset> provider) {
+            if (data instanceof HwmfCharsetAware) {
+                ((HwmfCharsetAware)data).setCharsetProvider(provider);
+            }
+        }
     }
 
     public static class EmfCommentDataIterator implements Iterator<EmfCommentData> {
@@ -250,8 +259,9 @@ public class HemfComment {
      * Private data is unknown to EMF; it is meaningful only to applications that know the format of the
      * data and how to use it. EMR_COMMENT private data records MAY be ignored.
      */
-    public static class EmfCommentDataGeneric implements EmfCommentData {
+    public static class EmfCommentDataGeneric implements EmfCommentData, HwmfCharsetAware {
         private byte[] privateData;
+        private Supplier<Charset> charsetProvider = () -> LocaleUtil.CHARSET_1252;
 
         @Override
         public HemfCommentRecordType getCommentRecordType() {
@@ -275,7 +285,7 @@ public class HemfComment {
         }
 
         public String getPrivateDataAsString() {
-            return new String(privateData, LocaleUtil.CHARSET_1252);
+            return new String(privateData, charsetProvider.get());
         }
 
         @Override
@@ -285,6 +295,11 @@ public class HemfComment {
                 "privateDataAsString", this::getPrivateDataAsString
             );
         }
+
+        @Override
+        public void setCharsetProvider(Supplier<Charset> provider) {
+            charsetProvider = provider;
+        }
     }
 
     /** The EMR_COMMENT_EMFPLUS record contains embedded EMF+ records. */
index e7d9947b726869db1095c668cdefedba477f8519..c10c004f51bace0a05ce99b344384a87d9cdcfeb 100644 (file)
@@ -206,6 +206,7 @@ public class HemfText {
             // the axis to convert from page space units to .01mm units.
             // This SHOULD be used only if the graphics mode specified by iGraphicsMode is GM_COMPATIBLE.
             Dimension2D scl = graphicsMode == EmfGraphicsMode.GM_COMPATIBLE ? scale : null;
+            ctx.setCharsetProvider(charsetProvider);
             ctx.drawString(rawTextBytes, stringLength, reference, scl, bounds, options, dx, isUnicode());
         }
 
index 5df76bde68f226e24bc6b6c37c0ddf0530e1d7ab..9f45de5e8a625e1556d9d08cded1c9784f9a2508 100644 (file)
@@ -28,6 +28,7 @@ import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
@@ -37,7 +38,6 @@ 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;
@@ -748,12 +748,12 @@ public final class HemfPlusDraw {
             }
 
             if (REALIZED_ADVANCE.isSet(optionsFlags)) {
-                byte[] buf = glyphs.getBytes(Charsets.UTF_16LE);
+                byte[] buf = glyphs.getBytes(StandardCharsets.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);
+                    byte[] buf = new String(new int[]{glyphIter.next()}, 0, 1).getBytes(StandardCharsets.UTF_16LE);
                     ctx.drawString(buf, buf.length, p, null, null, null, null, true);
                 });
             }
index b9ae90f0afa60cb71e6bc4683ca40a1e5cf5559d..c94fb8ac4bf532d6fe9e01f7672729557b9af355 100644 (file)
@@ -26,6 +26,7 @@ import java.awt.geom.AffineTransform;
 import java.awt.geom.Dimension2D;
 import java.awt.geom.Rectangle2D;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -41,10 +42,12 @@ 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.util.Dimension2DDouble;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianInputStream;
+import org.apache.poi.util.LocaleUtil;
 import org.apache.poi.util.Units;
 
 /**
@@ -55,6 +58,7 @@ public class HemfPicture implements Iterable<HemfRecord>, GenericRecord {
     private final LittleEndianInputStream stream;
     private final List<HemfRecord> records = new ArrayList<>();
     private boolean isParsed = false;
+    private Charset defaultCharset = LocaleUtil.CHARSET_1252;
 
     public HemfPicture(InputStream is) {
         this(new LittleEndianInputStream(is));
@@ -79,6 +83,9 @@ public class HemfPicture implements Iterable<HemfRecord>, GenericRecord {
                     header[0] = (HemfHeader) r;
                 }
                 r.setHeader(header[0]);
+                if (r instanceof HwmfCharsetAware) {
+                    ((HwmfCharsetAware)r).setCharsetProvider(this::getDefaultCharset);
+                }
                 records.add(r);
             });
         }
@@ -199,4 +206,12 @@ public class HemfPicture implements Iterable<HemfRecord>, GenericRecord {
     public Map<String, Supplier<?>> getGenericProperties() {
         return null;
     }
+
+    public void setDefaultCharset(Charset defaultCharset) {
+        this.defaultCharset = defaultCharset;
+    }
+
+    public Charset getDefaultCharset() {
+        return defaultCharset;
+    }
 }
index cf35b716ae6ed6243e3580c3f23bdd38153c97b1..38b473da03f3306f7f8e13de664a1f140639ae94 100644 (file)
@@ -40,6 +40,7 @@ import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.text.AttributedString;
 import java.util.ArrayList;
 import java.util.BitSet;
@@ -51,8 +52,9 @@ import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.TreeMap;
 import java.util.function.BiConsumer;
+import java.util.function.Supplier;
 
-import org.apache.commons.codec.Charsets;
+import org.apache.poi.common.usermodel.fonts.FontCharset;
 import org.apache.poi.common.usermodel.fonts.FontInfo;
 import org.apache.poi.hwmf.record.HwmfBrushStyle;
 import org.apache.poi.hwmf.record.HwmfFont;
@@ -64,6 +66,7 @@ import org.apache.poi.hwmf.record.HwmfPenStyle;
 import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
 import org.apache.poi.hwmf.record.HwmfRegionMode;
 import org.apache.poi.hwmf.record.HwmfText.WmfExtTextOutOptions;
+import org.apache.poi.hwmf.usermodel.HwmfCharsetAware;
 import org.apache.poi.sl.draw.BitmapImageRenderer;
 import org.apache.poi.sl.draw.DrawFactory;
 import org.apache.poi.sl.draw.DrawFontManager;
@@ -72,7 +75,7 @@ import org.apache.poi.sl.draw.ImageRenderer;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LocaleUtil;
 
-public class HwmfGraphics {
+public class HwmfGraphics implements HwmfCharsetAware {
 
     public enum FillDrawStyle {
         NONE(FillDrawStyle::fillNone),
@@ -128,9 +131,9 @@ public class HwmfGraphics {
     private final AffineTransform initialAT = new AffineTransform();
 
 
-    private static final Charset DEFAULT_CHARSET = LocaleUtil.CHARSET_1252;
     /** Bounding box from the placeable header */
     private final Rectangle2D bbox;
+    private Supplier<Charset> charsetProvider = () -> LocaleUtil.CHARSET_1252;
 
     /**
      * Initialize a graphics context for wmf rendering
@@ -595,16 +598,26 @@ public class HwmfGraphics {
         }
     }
 
-    private static Charset getCharset(HwmfFont font, boolean isUnicode) {
+    private Charset getCharset(HwmfFont font, boolean isUnicode) {
         if (isUnicode) {
-            return Charsets.UTF_16LE;
+            return StandardCharsets.UTF_16LE;
         }
 
-        Charset charset = font.getCharset().getCharset();
-        return (charset == null) ? DEFAULT_CHARSET : charset;
+        FontCharset fc = font.getCharset();
+        if (fc == FontCharset.DEFAULT) {
+            return charsetProvider.get();
+        }
+
+        Charset charset = fc.getCharset();
+        return (charset == null) ? charsetProvider.get() : charset;
     }
 
-    private static String trimText(HwmfFont font, boolean isUnicode, byte[] text, int length) {
+    @Override
+    public void setCharsetProvider(Supplier<Charset> provider) {
+        charsetProvider = provider;
+    }
+
+    private String trimText(HwmfFont font, boolean isUnicode, byte[] text, int length) {
         final Charset charset = getCharset(font, isUnicode);
 
         int trimLen;
@@ -717,7 +730,9 @@ public class HwmfGraphics {
                 graphicsCtx.fill(dstBounds);
                 break;
             default:
+            case SRCAND:
             case SRCCOPY:
+            case SRCINVERT:
                 if (img == null) {
                     return;
                 }
@@ -746,7 +761,20 @@ public class HwmfGraphics {
                 // the difference is, that clippings are 0-based, whereas the srcBounds are absolute in the user-space
                 // of the referenced image and can be also negative
                 Composite old = graphicsCtx.getComposite();
-                graphicsCtx.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
+                int newComp;
+                switch (prop.getRasterOp()) {
+                    default:
+                    case SRCCOPY:
+                        newComp = AlphaComposite.SRC_OVER;
+                        break;
+                    case SRCINVERT:
+                        newComp = AlphaComposite.SRC_IN;
+                        break;
+                    case SRCAND:
+                        newComp = AlphaComposite.SRC;
+                        break;
+                }
+                graphicsCtx.setComposite(AlphaComposite.getInstance(newComp));
 
                 boolean useDeviceBounds = (img instanceof HwmfImageRenderer);
 
index 68abb2432c204e4044f385555065aef2dc916318..9a44028f073c257dba97e3cde97975ef8ff7797f 100644 (file)
@@ -27,6 +27,7 @@ import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.Iterator;
 
 import org.apache.poi.common.usermodel.GenericRecord;
@@ -34,6 +35,7 @@ import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
 import org.apache.poi.hwmf.usermodel.HwmfPicture;
 import org.apache.poi.sl.draw.BitmapImageRenderer;
 import org.apache.poi.sl.draw.DrawPictureShape;
+import org.apache.poi.sl.draw.Drawable;
 import org.apache.poi.sl.draw.EmbeddedExtractor;
 import org.apache.poi.sl.draw.ImageRenderer;
 import org.apache.poi.sl.usermodel.PictureData.PictureType;
@@ -47,6 +49,7 @@ import org.apache.poi.util.Units;
 public class HwmfImageRenderer implements ImageRenderer, EmbeddedExtractor {
     HwmfPicture image;
     double alpha;
+    boolean charsetInitialized = false;
 
     @Override
     public boolean canRender(String contentType) {
@@ -87,9 +90,9 @@ public class HwmfImageRenderer implements ImageRenderer, EmbeddedExtractor {
     @Override
     public BufferedImage getImage(Dimension2D dim) {
         if (image == null) {
-            return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); 
+            return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
         }
-        
+
         BufferedImage bufImg = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB);
         Graphics2D g = bufImg.createGraphics();
         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
@@ -101,7 +104,7 @@ public class HwmfImageRenderer implements ImageRenderer, EmbeddedExtractor {
 
         return BitmapImageRenderer.setAlpha(bufImg, alpha);
     }
-    
+
     @Override
     public boolean drawImage(Graphics2D graphics, Rectangle2D anchor) {
         return drawImage(graphics, anchor, null);
@@ -113,6 +116,11 @@ public class HwmfImageRenderer implements ImageRenderer, EmbeddedExtractor {
             return false;
         }
 
+        Charset cs = (Charset)graphics.getRenderingHint(Drawable.DEFAULT_CHARSET);
+        if (cs != null && !charsetInitialized) {
+            setDefaultCharset(cs);
+        }
+
         HwmfGraphicsState graphicsState = new HwmfGraphicsState();
         graphicsState.backup(graphics);
 
@@ -185,4 +193,10 @@ public class HwmfImageRenderer implements ImageRenderer, EmbeddedExtractor {
     public Rectangle2D getBounds() {
         return Units.pointsToPixel(image == null ? new Rectangle2D.Double() : image.getBoundsInPoints());
     }
+
+    @Override
+    public void setDefaultCharset(Charset defaultCharset) {
+        image.setDefaultCharset(defaultCharset);
+        charsetInitialized = true;
+    }
 }
index bca708b7f7589204d5b09d9bc84685909854c098..85c2d4efe937af813258871a7bf5e869af418361 100644 (file)
@@ -485,11 +485,14 @@ public class HwmfBitmapDib implements GenericRecord {
 
         if (foreground != null && background != null && headerBitCount == HwmfBitmapDib.BitCount.BI_BITCOUNT_1) {
             IndexColorModel cmOld = (IndexColorModel)bi.getColorModel();
-            int transPixel = hasAlpha ? (((cmOld.getRGB(0) & 0xFFFFFF) == 0) ? 0 : 1) : -1;
+            int fg = foreground.getRGB();
+            int bg = background.getRGB() & (hasAlpha ? 0xFFFFFF : 0xFFFFFFFF);
+            boolean ordered = (cmOld.getRGB(0) & 0xFFFFFF) == (bg & 0xFFFFFF);
+            int transPixel = ordered ? 0 : 1;
+            int[] cmap = ordered ? new int[]{ bg, fg } : new int[]{ fg, bg };
             int transferType = bi.getData().getTransferType();
-            int fg = foreground.getRGB(), bg = background.getRGB();
-            int[] cmap = { (transPixel == 0 ? bg : fg), (transPixel == 1 ? bg : fg) };
-            IndexColorModel cmNew = new IndexColorModel(1, cmap.length, cmap, 0, hasAlpha, transPixel, transferType);
+
+            IndexColorModel cmNew = new IndexColorModel(1, 2, cmap, 0, hasAlpha, transPixel, transferType);
             bi = new BufferedImage(cmNew, bi.getRaster(), false, null);
         }
 
index 15f88d5e6ff493c9917a0a2e7e27d24214fffcf5..cc29087c6982adc216b5176e38be2f7ada2c6ae9 100644 (file)
@@ -35,6 +35,7 @@ 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.record.HwmfMisc.WmfSetMapMode;
+import org.apache.poi.hwmf.usermodel.HwmfCharsetAware;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
 import org.apache.poi.util.GenericRecordJsonWriter;
@@ -42,6 +43,7 @@ import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
+import org.apache.poi.util.LocaleUtil;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
@@ -172,7 +174,7 @@ public class HwmfText {
      * The META_TEXTOUT record outputs a character string at the specified location by using the font,
      * background color, and text color that are defined in the playback device context.
      */
-    public static class WmfTextOut implements HwmfRecord {
+    public static class WmfTextOut implements HwmfRecord, HwmfCharsetAware {
         /**
          * A 16-bit signed integer that defines the length of the string, in bytes, pointed to by String.
          */
@@ -189,6 +191,8 @@ public class HwmfText {
 
         protected Point2D reference = new Point2D.Double();
 
+        protected Supplier<Charset> charsetProvider = () -> LocaleUtil.CHARSET_1252;
+
         @Override
         public HwmfRecordType getWmfRecordType() {
             return HwmfRecordType.textOut;
@@ -211,6 +215,7 @@ public class HwmfText {
 
         @Override
         public void draw(HwmfGraphics ctx) {
+            ctx.setCharsetProvider(charsetProvider);
             ctx.drawString(getTextBytes(), stringLength, reference);
         }
 
@@ -235,6 +240,11 @@ public class HwmfText {
                 "reference", () -> reference
             );
         }
+
+        @Override
+        public void setCharsetProvider(Supplier<Charset> provider) {
+            charsetProvider = provider;
+        }
     }
 
     @SuppressWarnings("unused")
@@ -343,7 +353,7 @@ public class HwmfText {
      * are defined in the playback device context. Optionally, dimensions can be provided for clipping,
      * opaquing, or both.
      */
-    public static class WmfExtTextOut implements HwmfRecord {
+    public static class WmfExtTextOut implements HwmfRecord, HwmfCharsetAware {
         /**
          * The location, in logical units, where the text string is to be placed.
          */
@@ -383,6 +393,8 @@ public class HwmfText {
          */
         protected final List<Integer> dx = new ArrayList<>();
 
+        protected Supplier<Charset> charsetProvider = () -> LocaleUtil.CHARSET_1252;
+
         public WmfExtTextOut() {
             this(new WmfExtTextOutOptions());
         }
@@ -437,6 +449,7 @@ public class HwmfText {
 
         @Override
         public void draw(HwmfGraphics ctx) {
+            ctx.setCharsetProvider(charsetProvider);
             ctx.drawString(rawTextBytes, stringLength, reference, null, bounds, options, dx, false);
         }
 
@@ -445,8 +458,7 @@ public class HwmfText {
                 return "";
             }
             String ret = new String(rawTextBytes, charset);
-            return ret.substring(0,
-                    Math.min(ret.length(), stringLength));
+            return ret.substring(0, Math.min(ret.length(), stringLength));
         }
 
         public Point2D getReference() {
@@ -468,7 +480,7 @@ public class HwmfText {
 
         private String getGenericText() {
             try {
-                return getText(isUnicode() ? StandardCharsets.UTF_16LE : StandardCharsets.US_ASCII);
+                return getText(isUnicode() ? StandardCharsets.UTF_16LE : charsetProvider.get());
             } catch (IOException e) {
                 return "";
             }
@@ -483,6 +495,11 @@ public class HwmfText {
                 "dx", () -> dx
             );
         }
+
+        @Override
+        public void setCharsetProvider(Supplier<Charset> provider) {
+            charsetProvider = provider;
+        }
     }
 
     public enum HwmfTextAlignment {
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfCharsetAware.java b/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfCharsetAware.java
new file mode 100644 (file)
index 0000000..1897278
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *  ====================================================================
+ *    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.usermodel;
+
+import java.nio.charset.Charset;
+import java.util.function.Supplier;
+
+/**
+ * Helper interface to provide a default charset to records which would depend on a system charset
+ */
+public interface HwmfCharsetAware {
+    void setCharsetProvider(Supplier<Charset> provider);
+}
index a33ea0ff1de91277710d264b1c70c33c93a76d73..3eccdb68001913438a91f2150f9f296de7ffdbf6 100644 (file)
@@ -24,6 +24,7 @@ import java.awt.geom.Dimension2D;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -46,6 +47,7 @@ import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowOrg;
 import org.apache.poi.util.Dimension2DDouble;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndianInputStream;
+import org.apache.poi.util.LocaleUtil;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 import org.apache.poi.util.RecordFormatException;
@@ -56,11 +58,13 @@ public class HwmfPicture implements Iterable<HwmfRecord>, GenericRecord {
     public static final int MAX_RECORD_LENGTH = 50_000_000;
 
     private static final POILogger logger = POILogFactory.getLogger(HwmfPicture.class);
-    
+
     final List<HwmfRecord> records = new ArrayList<>();
     final HwmfPlaceableHeader placeableHeader;
     final HwmfHeader header;
-    
+    /** The default charset */
+    private Charset defaultCharset = LocaleUtil.CHARSET_1252;
+
     public HwmfPicture(InputStream inputStream) throws IOException {
 
         try (LittleEndianInputStream leis = new LittleEndianInputStream(inputStream)) {
@@ -110,6 +114,10 @@ public class HwmfPicture implements Iterable<HwmfRecord>, GenericRecord {
                         throw new RecordFormatException("Tried to skip "+remainingSize + " but skipped: "+skipped);
                     }
                 }
+
+                if (wr instanceof HwmfCharsetAware) {
+                    ((HwmfCharsetAware)wr).setCharsetProvider(this::getDefaultCharset);
+                }
             }
         }
     }
@@ -126,7 +134,7 @@ public class HwmfPicture implements Iterable<HwmfRecord>, GenericRecord {
         Rectangle2D bounds = new Rectangle2D.Double(0,0,width,height);
         draw(ctx, bounds);
     }
-    
+
     public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) {
         HwmfGraphicsState state = new HwmfGraphicsState();
         state.backup(ctx);
@@ -198,7 +206,7 @@ public class HwmfPicture implements Iterable<HwmfRecord>, GenericRecord {
             }
             if (wOrg != null && wExt != null) {
                 return new Rectangle2D.Double(wOrg.getX(), wOrg.getY(), wExt.getSize().getWidth(), wExt.getSize().getHeight());
-        }
+            }
         }
         return null;
     }
@@ -260,4 +268,12 @@ public class HwmfPicture implements Iterable<HwmfRecord>, GenericRecord {
     public List<? extends GenericRecord> getGenericChildren() {
         return getRecords();
     }
+
+    public void setDefaultCharset(Charset defaultCharset) {
+        this.defaultCharset = defaultCharset;
+    }
+
+    public Charset getDefaultCharset() {
+        return defaultCharset;
+    }
 }
index 30e0059f080f27be44986c185a171754dc077736..0b1261a7fc65566215aa82e69fe8c94692d9adbf 100644 (file)
 
 package org.apache.poi.poifs.filesystem;
 
-import org.apache.commons.codec.Charsets;
-import org.apache.poi.POIDataSamples;
-import org.apache.poi.util.TempFile;
-import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.BufferedInputStream;
 import java.io.File;
@@ -28,32 +31,35 @@ import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Random;
 
-import static org.junit.Assert.*;
+import org.apache.poi.POIDataSamples;
+import org.apache.poi.util.TempFile;
+import org.junit.Test;
 
 public class TestFileMagic {
     @Test
     public void testFileMagic() {
         assertEquals(FileMagic.XML, FileMagic.valueOf("XML"));
-        assertEquals(FileMagic.XML, FileMagic.valueOf("<?xml".getBytes(Charsets.UTF_8)));
+        assertEquals(FileMagic.XML, FileMagic.valueOf("<?xml".getBytes(StandardCharsets.UTF_8)));
 
         assertEquals(FileMagic.HTML, FileMagic.valueOf("HTML"));
-        assertEquals(FileMagic.HTML, FileMagic.valueOf("<!DOCTYP".getBytes(Charsets.UTF_8)));
-        assertEquals(FileMagic.HTML, FileMagic.valueOf("<!DOCTYPE".getBytes(Charsets.UTF_8)));
-        assertEquals(FileMagic.HTML, FileMagic.valueOf("<html".getBytes(Charsets.UTF_8)));
-        assertEquals(FileMagic.HTML, FileMagic.valueOf("\n\r<html".getBytes(Charsets.UTF_8)));
-        assertEquals(FileMagic.HTML, FileMagic.valueOf("\n<html".getBytes(Charsets.UTF_8)));
-        assertEquals(FileMagic.HTML, FileMagic.valueOf("\r\n<html".getBytes(Charsets.UTF_8)));
-        assertEquals(FileMagic.HTML, FileMagic.valueOf("\r<html".getBytes(Charsets.UTF_8)));
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("<!DOCTYP".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("<!DOCTYPE".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("<html".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("\n\r<html".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("\n<html".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("\r\n<html".getBytes(StandardCharsets.UTF_8)));
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("\r<html".getBytes(StandardCharsets.UTF_8)));
 
         assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xDB }));
         assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xE0, 'a', 'b', 'J', 'F', 'I', 'F', 0x00, 0x01 }));
         assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xEE }));
         assertEquals(FileMagic.JPEG, FileMagic.valueOf(new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xE1, 'd', 'c', 'E', 'x', 'i', 'f', 0x00, 0x00 }));
 
-        assertEquals(FileMagic.UNKNOWN, FileMagic.valueOf("something".getBytes(Charsets.UTF_8)));
+        assertEquals(FileMagic.UNKNOWN, FileMagic.valueOf("something".getBytes(StandardCharsets.UTF_8)));
         assertEquals(FileMagic.UNKNOWN, FileMagic.valueOf(new byte[0]));
 
         try {
diff --git a/test-data/slideshow/bug64693.pptx b/test-data/slideshow/bug64693.pptx
new file mode 100644 (file)
index 0000000..0f0d231
Binary files /dev/null and b/test-data/slideshow/bug64693.pptx differ