From 861988bac38a242d4b6b6bcb2ec9b6ea94ad4edc Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sun, 23 Jun 2019 22:21:15 +0000 Subject: [PATCH] Bug 60656 - EMF image support in slideshows git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1861952 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/sl/draw/BitmapImageRenderer.java | 3 +- .../org/apache/poi/sl/draw/DrawPaint.java | 58 ++++++------ .../apache/poi/sl/draw/DrawPictureShape.java | 7 +- .../apache/poi/sl/draw/DrawSimpleShape.java | 17 +--- .../org/apache/poi/sl/draw/ImageRenderer.java | 6 +- src/java/org/apache/poi/util/Units.java | 13 ++- .../poi/xslf/draw/SVGImageRenderer.java | 13 +-- .../poi/xslf/usermodel/XSLFPictureShape.java | 4 +- .../apache/poi/xslf/usermodel/XSLFShape.java | 11 ++- .../org/apache/poi/xslf/util/PPTX2PNG.java | 81 +++++++++++------ .../poi/xslf/usermodel/TestPPTX2PNG.java | 3 +- .../poi/hemf/draw/HemfImageRenderer.java | 13 +-- .../poi/hemf/usermodel/HemfPicture.java | 7 +- .../apache/poi/hslf/usermodel/HSLFFill.java | 12 +-- .../apache/poi/hwmf/draw/HwmfGraphics.java | 70 ++++---------- .../poi/hwmf/draw/HwmfImageRenderer.java | 14 +-- .../org/apache/poi/hwmf/record/HwmfFill.java | 32 +++++-- .../poi/hwmf/record/HwmfRegionMode.java | 85 +++++++++++++++-- .../apache/poi/hwmf/record/HwmfWindowing.java | 34 +++---- .../poi/hwmf/usermodel/HwmfPicture.java | 91 +++++++++++++------ .../org/apache/poi/hwmf/TestHwmfParsing.java | 35 ++++--- 21 files changed, 366 insertions(+), 243 deletions(-) diff --git a/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java b/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java index 145791f401..140fc777f2 100644 --- a/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java +++ b/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java @@ -23,6 +23,7 @@ import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Shape; import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; @@ -237,7 +238,7 @@ public class BitmapImageRenderer implements ImageRenderer { } @Override - public BufferedImage getImage(Dimension dim) { + public BufferedImage getImage(Dimension2D dim) { double w_old = img.getWidth(); double h_old = img.getHeight(); BufferedImage scaled = new BufferedImage((int)w_old, (int)h_old, BufferedImage.TYPE_INT_ARGB); diff --git a/src/java/org/apache/poi/sl/draw/DrawPaint.java b/src/java/org/apache/poi/sl/draw/DrawPaint.java index f012161b3c..fea1da4661 100644 --- a/src/java/org/apache/poi/sl/draw/DrawPaint.java +++ b/src/java/org/apache/poi/sl/draw/DrawPaint.java @@ -234,46 +234,50 @@ public class DrawPaint { @SuppressWarnings("WeakerAccess") protected Paint getTexturePaint(TexturePaint fill, Graphics2D graphics) { - InputStream is = fill.getImageData(); - if (is == null) { - return TRANSPARENT; - } assert(graphics != null); - ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, fill.getContentType()); + final String contentType = fill.getContentType(); - try { - try { - renderer.loadImage(is, fill.getContentType()); - } finally { - is.close(); - } - } catch (IOException e) { - LOG.log(POILogger.ERROR, "Can't load image data - using transparent color", e); - return TRANSPARENT; - } + ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, contentType); int alpha = fill.getAlpha(); if (0 <= alpha && alpha < 100000) { renderer.setAlpha(alpha/100000.f); } + // TODO: handle tile settings, currently the pattern is always streched 100% in height/width Rectangle2D textAnchor = shape.getAnchor(); - BufferedImage image; - if ("image/x-wmf".equals(fill.getContentType())) { - // don't rely on wmf dimensions, use dimension of anchor - // TODO: check pixels vs. points for image dimension - image = renderer.getImage(new Dimension((int)textAnchor.getWidth(), (int)textAnchor.getHeight())); - } else { - image = renderer.getImage(); - } - if(image == null) { - LOG.log(POILogger.ERROR, "Can't load image data"); + try (InputStream is = fill.getImageData()) { + if (is == null) { + return TRANSPARENT; + } + + renderer.loadImage(is, contentType); + + final BufferedImage image; + switch (contentType) { + case "image/x-wmf": + case "image/x-emf": + // don't rely on wmf dimensions, use dimension of anchor + // TODO: check pixels vs. points for image dimension + image = renderer.getImage(new Dimension((int)textAnchor.getWidth(), (int)textAnchor.getHeight())); + break; + default: + image = renderer.getImage(); + break; + } + + if(image == null) { + LOG.log(POILogger.ERROR, "Can't load image data"); + return TRANSPARENT; + } + + return new java.awt.TexturePaint(image, textAnchor); + } catch (IOException e) { + LOG.log(POILogger.ERROR, "Can't load image data - using transparent color", e); return TRANSPARENT; } - - return new java.awt.TexturePaint(image, textAnchor); } /** diff --git a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java index 84e7c91b33..84d3ab785a 100644 --- a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java @@ -56,9 +56,10 @@ public class DrawPictureShape extends DrawSimpleShape { } try { - ImageRenderer renderer = getImageRenderer(graphics, data.getContentType()); - if (renderer.canRender(data.getContentType())) { - renderer.loadImage(data.getData(), data.getContentType()); + String ct = data.getContentType(); + ImageRenderer renderer = getImageRenderer(graphics, ct); + if (renderer.canRender(ct)) { + renderer.loadImage(data.getData(), ct); renderer.drawImage(graphics, anchor, insets); return; } diff --git a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java index d535d71fa6..3b32fb3152 100644 --- a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java @@ -27,23 +27,10 @@ import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; -import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.EventFilter; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; - -import org.apache.poi.sl.draw.binding.CTCustomGeometry2D; + import org.apache.poi.sl.draw.geom.Context; import org.apache.poi.sl.draw.geom.CustomGeometry; import org.apache.poi.sl.draw.geom.Outline; @@ -54,8 +41,6 @@ import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; import org.apache.poi.sl.usermodel.Shadow; import org.apache.poi.sl.usermodel.SimpleShape; -import org.apache.poi.util.IOUtils; -import org.apache.poi.util.StaxHelper; import org.apache.poi.util.Units; diff --git a/src/java/org/apache/poi/sl/draw/ImageRenderer.java b/src/java/org/apache/poi/sl/draw/ImageRenderer.java index 330c02ab01..dd4e875b10 100644 --- a/src/java/org/apache/poi/sl/draw/ImageRenderer.java +++ b/src/java/org/apache/poi/sl/draw/ImageRenderer.java @@ -18,9 +18,9 @@ */ package org.apache.poi.sl.draw; -import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Insets; +import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; @@ -101,7 +101,7 @@ public interface ImageRenderer { /** * @return the dimension of the buffered image */ - Dimension getDimension(); + Dimension2D getDimension(); /** * @param alpha the alpha [0..1] to be added to the image (possibly already containing an alpha channel) @@ -119,7 +119,7 @@ public interface ImageRenderer { * * @since POI 3.15-beta2 */ - BufferedImage getImage(Dimension dim); + BufferedImage getImage(Dimension2D dim); /** * Render picture data into the supplied graphics diff --git a/src/java/org/apache/poi/util/Units.java b/src/java/org/apache/poi/util/Units.java index 709e3f10cc..a2e85e0aa0 100644 --- a/src/java/org/apache/poi/util/Units.java +++ b/src/java/org/apache/poi/util/Units.java @@ -16,9 +16,8 @@ ==================================================================== */ package org.apache.poi.util; -/** - * @author Yegor Kozlov - */ +import java.awt.geom.Dimension2D; + public class Units { /** * In Escher absolute distances are specified in @@ -146,7 +145,13 @@ public class Units { points /= PIXEL_DPI; return points; } - + + public static Dimension2D pointsToPixel(Dimension2D pointsDim) { + double width = pointsDim.getWidth() * PIXEL_DPI / POINT_DPI; + double height = pointsDim.getHeight() * PIXEL_DPI / POINT_DPI; + return new Dimension2DDouble(width, height); + } + public static int charactersToEMU(double characters) { return (int) characters * EMU_PER_CHARACTER; } diff --git a/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java b/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java index 4fc144f570..22250c56b6 100644 --- a/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java +++ b/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java @@ -17,11 +17,11 @@ package org.apache.poi.xslf.draw; -import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; @@ -42,6 +42,7 @@ import org.apache.batik.util.XMLResourceDescriptor; import org.apache.poi.sl.draw.Drawable; import org.apache.poi.sl.draw.ImageRenderer; import org.apache.poi.sl.usermodel.PictureData; +import org.apache.poi.util.Dimension2DDouble; import org.w3c.dom.Document; public class SVGImageRenderer implements ImageRenderer { @@ -75,9 +76,9 @@ public class SVGImageRenderer implements ImageRenderer { } @Override - public Dimension getDimension() { + public Dimension2D getDimension() { Rectangle2D r = svgRoot.getPrimitiveBounds(); - return new Dimension((int)Math.ceil(r.getWidth()), (int)Math.ceil(r.getHeight())); + return new Dimension2DDouble(Math.ceil(r.getWidth()), Math.ceil(r.getHeight())); } @Override @@ -91,7 +92,7 @@ public class SVGImageRenderer implements ImageRenderer { } @Override - public BufferedImage getImage(Dimension dim) { + public BufferedImage getImage(Dimension2D dim) { BufferedImage bi = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = (Graphics2D) bi.getGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); @@ -101,7 +102,7 @@ public class SVGImageRenderer implements ImageRenderer { g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2d.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE, new WeakReference(bi)); - Dimension dimSVG = getDimension(); + Dimension2D dimSVG = getDimension(); double scaleX = dim.getWidth() / dimSVG.getWidth(); double scaleY = dim.getHeight() / dimSVG.getHeight(); @@ -122,7 +123,7 @@ public class SVGImageRenderer implements ImageRenderer { public boolean drawImage(Graphics2D graphics, Rectangle2D anchor, Insets clip) { graphics.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE, graphics.getRenderingHint(Drawable.BUFFERED_IMAGE)); - Dimension bounds = getDimension(); + Dimension2D bounds = getDimension(); AffineTransform at = new AffineTransform(); at.translate(anchor.getX(), anchor.getY()); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java index 9c4e23f060..1a0c96fa30 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java @@ -21,8 +21,8 @@ package org.apache.poi.xslf.usermodel; import static org.apache.poi.openxml4j.opc.PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS; -import java.awt.Dimension; import java.awt.Insets; +import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; @@ -305,7 +305,7 @@ public class XSLFPictureShape extends XSLFSimpleShape renderer.loadImage(is, svgPic.getType().contentType); } - Dimension dim = renderer.getDimension(); + Dimension2D dim = renderer.getDimension(); Rectangle2D anc = (anchor != null) ? anchor : new Rectangle2D.Double(0,0, Units.pixelToPoints((int)dim.getWidth()), Units.pixelToPoints((int)dim.getHeight())); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java index 8227b46460..8b056847b8 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java @@ -60,6 +60,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrix; import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference; import org.openxmlformats.schemas.drawingml.x2006.main.STPathShadeType; import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; @@ -149,6 +150,15 @@ public abstract class XSLFShape implements Shape { PropertyFetcher fetcher = new PropertyFetcher() { @Override public boolean fetch(XSLFShape shape) { + PackagePart pp = shape.getSheet().getPackagePart(); + if (shape instanceof XSLFPictureShape) { + CTPicture pic = (CTPicture)shape.getXmlObject(); + if (pic.getBlipFill() != null) { + setValue(selectPaint(pic.getBlipFill(), pp)); + return true; + } + } + XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(shape.getShapeProperties()); if (fp == null) { return false; @@ -159,7 +169,6 @@ public abstract class XSLFShape implements Shape { return true; } - PackagePart pp = shape.getSheet().getPackagePart(); PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder); if (paint != null) { setValue(paint); diff --git a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java index 1de2b1bea8..b8bea4aea0 100644 --- a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java +++ b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Locale; import java.util.Set; import java.util.TreeSet; +import java.util.regex.Pattern; import javax.imageio.ImageIO; @@ -39,21 +40,30 @@ import org.apache.poi.sl.usermodel.SlideShowFactory; /** * An utility to convert slides of a .pptx slide show to a PNG image - * - * @author Yegor Kozlov */ public class PPTX2PNG { - static void usage(String error){ + private static final String INPUT_PAT_REGEX = + "(?[^|]+)\\|(?[^|]+)\\|(?.+)\\.(?[^.]++)"; + + private static final Pattern INPUT_PATTERN = Pattern.compile(INPUT_PAT_REGEX); + + private static final String OUTPUT_PAT_REGEX = "${basename}-${slideno}.${format}"; + + + private static void usage(String error){ String msg = "Usage: PPTX2PNG [options] \n" + (error == null ? "" : ("Error: "+error+"\n")) + "Options:\n" + - " -scale scale factor\n" + - " -slide 1-based index of a slide to render\n" + - " -format png,gif,jpg (,null for testing)" + - " -outdir output directory, defaults to origin of the ppt/pptx file" + - " -quiet do not write to console (for normal processing)"; + " -scale scale factor\n" + + " -slide 1-based index of a slide to render\n" + + " -format png,gif,jpg (,null for testing)\n" + + " -outdir output directory, defaults to origin of the ppt/pptx file\n" + + " -outfile output filename, defaults to '"+OUTPUT_PAT_REGEX+"'\n" + + " -outpat output filename pattern, defaults to '"+OUTPUT_PAT_REGEX+"'\n" + + " patterns: basename, slideno, format, ext\n" + + " -quiet do not write to console (for normal processing)"; System.out.println(msg); // no System.exit here, as we also run in junit tests! @@ -70,23 +80,43 @@ public class PPTX2PNG { File file = null; String format = "png"; File outdir = null; + String outfile = null; boolean quiet = false; + String outpattern = OUTPUT_PAT_REGEX; for (int i = 0; i < args.length; i++) { - if (args[i].startsWith("-")) { - if ("-scale".equals(args[i])) { - scale = Float.parseFloat(args[++i]); // lgtm[java/index-out-of-bounds] - } else if ("-slide".equals(args[i])) { - slidenumStr = args[++i]; // lgtm[java/index-out-of-bounds] - } else if ("-format".equals(args[i])) { - format = args[++i]; // lgtm[java/index-out-of-bounds] - } else if ("-outdir".equals(args[i])) { - outdir = new File(args[++i]); // lgtm[java/index-out-of-bounds] - } else if ("-quiet".equals(args[i])) { + String opt = (i+1 < args.length) ? args[i+1] : null; + switch (args[i]) { + case "-scale": + scale = Float.parseFloat(opt); + i++; + break; + case "-slide": + slidenumStr = opt; + i++; + break; + case "-format": + format = opt; + i++; + break; + case "-outdir": + outdir = new File(opt); + i++; + break; + case "-outfile": + outfile = opt; + i++; + break; + case "-outpat": + outpattern = opt; + i++; + break; + case "-quiet": quiet = true; - } - } else { - file = new File(args[i]); + break; + default: + file = new File(args[i]); + break; } } @@ -135,7 +165,7 @@ public class PPTX2PNG { Slide slide = slides.get(slideNo); String title = slide.getTitle(); if (!quiet) { - System.out.println("Rendering slide " + slideNo + (title == null ? "" : ": " + title)); + System.out.println("Rendering slide " + (slideNo+1) + (title == null ? "" : ": " + title.trim())); } BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); @@ -155,10 +185,9 @@ public class PPTX2PNG { // save the result if (!"null".equals(format)) { - String outname = file.getName().replaceFirst(".pptx?", ""); - outname = String.format(Locale.ROOT, "%1$s-%2$04d.%3$s", outname, slideNo, format); - File outfile = new File(outdir, outname); - ImageIO.write(img, format, outfile); + String inname = String.format(Locale.ROOT, "%04d|%s|%s", slideNo+1, format, file.getName()); + String outname = (outfile != null) ? outfile : INPUT_PATTERN.matcher(inname).replaceAll(outpattern); + ImageIO.write(img, format, new File(outdir, outname)); } graphics.dispose(); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java index 9a206a48ca..62c5503dbd 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java @@ -94,9 +94,10 @@ public class TestPPTX2PNG { assumeFalse("ignore HSLF / .ppt files in no-scratchpad run", xslfOnly && pptFile.toLowerCase(Locale.ROOT).endsWith("ppt")); String[] args = { - "-format", "null", // png,gif,jpg or null for test + "-format", "png", // png,gif,jpg or null for test "-slide", "-1", // -1 for all "-outdir", new File("build/tmp/").getCanonicalPath(), + "-outpat", "${basename}-${slideno}-${ext}.${format}", "-quiet", (basedir == null ? samples.getFile(pptFile) : new File(basedir, pptFile)).getAbsolutePath() }; diff --git a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java index 712c2e6c0e..3a4ea04c4c 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java +++ b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java @@ -60,15 +60,8 @@ public class HemfImageRenderer implements ImageRenderer { } @Override - public Dimension getDimension() { - int width = 0, height = 0; - if (image != null) { - Dimension2D dim = image.getSize(); - width = Units.pointsToPixel(dim.getWidth()); - // keep aspect ratio for height - height = Units.pointsToPixel(dim.getHeight()); - } - return new Dimension(width, height); + public Dimension2D getDimension() { + return Units.pointsToPixel(image == null ? new Dimension() : image.getSize()); } @Override @@ -82,7 +75,7 @@ public class HemfImageRenderer implements ImageRenderer { } @Override - public BufferedImage getImage(Dimension dim) { + public BufferedImage getImage(Dimension2D dim) { if (image == null) { return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); } diff --git a/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java b/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java index 6e062cdc48..d96f0c24cf 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java +++ b/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java @@ -19,6 +19,7 @@ package org.apache.poi.hemf.usermodel; import java.awt.Graphics2D; +import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; @@ -152,9 +153,10 @@ public class HemfPicture implements Iterable { } public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) { - HemfHeader header = (HemfHeader)getRecords().get(0); + final HemfHeader header = (HemfHeader)getRecords().get(0); - AffineTransform at = ctx.getTransform(); + final Shape clip = ctx.getClip(); + final AffineTransform at = ctx.getTransform(); try { Rectangle2D emfBounds = header.getBoundsRectangle(); @@ -175,6 +177,7 @@ public class HemfPicture implements Iterable { } } finally { ctx.setTransform(at); + ctx.setClip(clip); } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java index 63885af3f3..251c4172f0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java @@ -415,9 +415,9 @@ public final class HSLFFill { EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); int propVal = (p == null) ? 0 : p.getPropertyValue(); - return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal)) - ? null - : shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY); + return (FILL_USE_FILLED.isSet(propVal) && FILL_FILLED.isSet(propVal)) + ? shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY) + : null; } /** @@ -459,9 +459,9 @@ public final class HSLFFill { EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); int propVal = (p == null) ? 0 : p.getPropertyValue(); - return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal)) - ? null - : shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY); + return (FILL_USE_FILLED.isSet(propVal) && FILL_FILLED.isSet(propVal)) + ? shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY) + : null; } /** diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java index a4c2774945..0a3eecd00c 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java @@ -43,6 +43,7 @@ import java.util.BitSet; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.TreeMap; import org.apache.commons.codec.Charsets; @@ -352,8 +353,9 @@ public class HwmfGraphics { case MM_ISOTROPIC: // TODO: to be validated ... // like anisotropic, but use x-axis as reference + graphicsCtx.translate(bbox.getCenterX(), bbox.getCenterY()); graphicsCtx.scale(bbox.getWidth()/win.getWidth(), bbox.getWidth()/win.getWidth()); - graphicsCtx.translate(-win.getX(), -win.getY()); + graphicsCtx.translate(-win.getCenterX(), -win.getCenterY()); break; case MM_LOMETRIC: case MM_HIMETRIC: @@ -407,11 +409,8 @@ public class HwmfGraphics { } } - String textString = ""; - if (text != null) { - textString = new String(text, charset).trim(); - textString = textString.substring(0, Math.min(textString.length(), length)); - } + String textString = new String(text, charset).trim(); + textString = textString.substring(0, Math.min(textString.length(), length)); if (textString.isEmpty()) { return; @@ -504,7 +503,7 @@ public class HwmfGraphics { final Shape clipShape = graphicsCtx.getClip(); try { - if (clip != null) { + if (clip != null && !clip.getBounds2D().isEmpty()) { graphicsCtx.translate(-clip.getCenterX(), -clip.getCenterY()); graphicsCtx.rotate(angle); graphicsCtx.translate(clip.getCenterX(), clip.getCenterY()); @@ -647,59 +646,28 @@ public class HwmfGraphics { graphicsCtx.setTransform(tx); } - private static int clipCnt = 0; - + /** + * Set the new clipping region + * + * @param clip the next clipping region to be processed + * @param regionMode the mode and operation of how to apply the next clipping region + * @param useInitialAT if true, the clipping is applied on the initial (world) coordinate system + */ public void setClip(Shape clip, HwmfRegionMode regionMode, boolean useInitialAT) { final AffineTransform at = graphicsCtx.getTransform(); if (useInitialAT) { graphicsCtx.setTransform(initialAT); } + final Shape oldClip = graphicsCtx.getClip(); - final boolean isEmpty = clip.getBounds2D().isEmpty(); - switch (regionMode) { - case RGN_AND: - if (!isEmpty) { - graphicsCtx.clip(clip); - } - break; - case RGN_OR: - if (!isEmpty) { - if (oldClip == null) { - graphicsCtx.setClip(clip); - } else { - Area area = new Area(oldClip); - area.add(new Area(clip)); - graphicsCtx.setClip(area); - } - } - break; - case RGN_XOR: - if (!isEmpty) { - if (oldClip == null) { - graphicsCtx.setClip(clip); - } else { - Area area = new Area(oldClip); - area.exclusiveOr(new Area(clip)); - graphicsCtx.setClip(area); - } - } - break; - case RGN_DIFF: - if (!isEmpty) { - if (oldClip != null) { - Area area = new Area(oldClip); - area.subtract(new Area(clip)); - graphicsCtx.setClip(area); - } - } - break; - case RGN_COPY: { - graphicsCtx.setClip(isEmpty ? null : clip); - break; - } + final Shape newClip = regionMode.applyOp(oldClip, clip); + if (!Objects.equals(oldClip, newClip)) { + graphicsCtx.setClip(newClip); } + if (useInitialAT) { graphicsCtx.setTransform(at); } + prop.setClip(graphicsCtx.getClip()); } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java index a87db042d6..f30bdc34d2 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java @@ -21,6 +21,7 @@ import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.RenderingHints; +import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.RescaleOp; @@ -64,15 +65,8 @@ public class HwmfImageRenderer implements ImageRenderer { } @Override - public Dimension getDimension() { - int width = 0, height = 0; - if (image != null) { - Dimension dim = image.getSize(); - width = Units.pointsToPixel(dim.getWidth()); - // keep aspect ratio for height - height = Units.pointsToPixel(dim.getHeight()); - } - return new Dimension(width, height); + public Dimension2D getDimension() { + return Units.pointsToPixel(image == null ? new Dimension() : image.getSize()); } @Override @@ -86,7 +80,7 @@ public class HwmfImageRenderer implements ImageRenderer { } @Override - public BufferedImage getImage(Dimension dim) { + public BufferedImage getImage(Dimension2D dim) { if (image == null) { return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java index 920e302e2a..20c48b8ee1 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java @@ -715,7 +715,17 @@ public class HwmfFill { } } - public static class WmfDibStretchBlt implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry { + /** + * The META_DIBSTRETCHBLT record specifies the transfer of a block of pixels in device-independent format + * according to a raster operation, with possible expansion or contraction. + * + * The destination of the transfer is the current output region in the playback device context. + * There are two forms of META_DIBSTRETCHBLT, one which specifies a device-independent bitmap (DIB) as the source, + * and the other which uses the playback device context as the source. Definitions follow for the fields that are + * the same in the two forms of META_DIBSTRETCHBLT. The subsections that follow specify the packet structures of + * the two forms of META_DIBSTRETCHBLT. + */ + public static class WmfDibStretchBlt implements HwmfRecord, HwmfImageRecord { /** * A 32-bit unsigned integer that defines how the source pixels, the current brush * in the playback device context, and the destination pixels are to be combined to form the @@ -748,7 +758,7 @@ public class HwmfFill { int rasterOpIndex = leis.readUShort(); rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex); - assert(rasterOpCode == rasterOperation.opCode); + assert(rasterOperation != null && rasterOpCode == rasterOperation.opCode); int size = 2*LittleEndianConsts.SHORT_SIZE; @@ -769,12 +779,18 @@ public class HwmfFill { @Override public void draw(HwmfGraphics ctx) { - ctx.addObjectTableEntry(this); - } - - @Override - public void applyObject(HwmfGraphics ctx) { - + HwmfDrawProperties prop = ctx.getProperties(); + prop.setRasterOp(rasterOperation); + // TODO: implement second operation based on playback device context + if (target != null) { + HwmfBkMode mode = prop.getBkMode(); + prop.setBkMode(HwmfBkMode.TRANSPARENT); + Color fgColor = prop.getPenColor().getColor(); + Color bgColor = prop.getBackgroundColor().getColor(); + BufferedImage bi = target.getImage(fgColor, bgColor, true); + ctx.drawImage(bi, srcBounds, dstBounds); + prop.setBkMode(mode); + } } @Override diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRegionMode.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRegionMode.java index 3a56e52e70..d6bc3fcb07 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRegionMode.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRegionMode.java @@ -17,37 +17,42 @@ package org.apache.poi.hwmf.record; -import org.apache.poi.hemf.record.emf.HemfFill; +import java.awt.Shape; +import java.awt.geom.Area; +import java.util.function.BiFunction; public enum HwmfRegionMode { /** * The new clipping region includes the intersection (overlapping areas) * of the current clipping region and the current path (or new region). */ - RGN_AND(0x01), + RGN_AND(0x01, HwmfRegionMode::andOp), /** * The new clipping region includes the union (combined areas) * of the current clipping region and the current path (or new region). */ - RGN_OR(0x02), + RGN_OR(0x02, HwmfRegionMode::orOp), /** * The new clipping region includes the union of the current clipping region * and the current path (or new region) but without the overlapping areas */ - RGN_XOR(0x03), + RGN_XOR(0x03, HwmfRegionMode::xorOp), /** * The new clipping region includes the areas of the current clipping region * with those of the current path (or new region) excluded. */ - RGN_DIFF(0x04), + RGN_DIFF(0x04, HwmfRegionMode::diffOp), /** * The new clipping region is the current path (or the new region). */ - RGN_COPY(0x05); + RGN_COPY(0x05, HwmfRegionMode::copyOp); - int flag; - HwmfRegionMode(int flag) { + private final int flag; + private final BiFunction op; + + HwmfRegionMode(int flag, BiFunction op) { this.flag = flag; + this.op = op; } public static HwmfRegionMode valueOf(int flag) { @@ -56,4 +61,68 @@ public enum HwmfRegionMode { } return null; } + + public int getFlag() { + return flag; + } + + public Shape applyOp(Shape oldClip, Shape newClip) { + return op.apply(oldClip, newClip); + } + + private static Shape andOp(final Shape oldClip, final Shape newClip) { + assert(newClip != null); + if (newClip.getBounds2D().isEmpty()) { + return oldClip; + } else if (oldClip == null) { + return newClip; + } else { + Area newArea = new Area(oldClip); + newArea.intersect(new Area(newClip)); + return newArea.getBounds2D().isEmpty() ? newClip : newArea; + } + } + + private static Shape orOp(final Shape oldClip, final Shape newClip) { + assert(newClip != null); + if (newClip.getBounds2D().isEmpty()) { + return oldClip; + } else if (oldClip == null) { + return newClip; + } else { + Area newArea = new Area(oldClip); + newArea.add(new Area(newClip)); + return newArea; + } + } + + private static Shape xorOp(final Shape oldClip, final Shape newClip) { + assert(newClip != null); + if (newClip.getBounds2D().isEmpty()) { + return oldClip; + } else if (oldClip == null) { + return newClip; + } else { + Area newArea = new Area(oldClip); + newArea.exclusiveOr(new Area(newClip)); + return newArea; + } + } + + private static Shape diffOp(final Shape oldClip, final Shape newClip) { + assert(newClip != null); + if (newClip.getBounds2D().isEmpty()) { + return oldClip; + } else if (oldClip == null) { + return newClip; + } else { + Area newArea = new Area(oldClip); + newArea.subtract(new Area(newClip)); + return newArea; + } + } + + private static Shape copyOp(final Shape oldClip, final Shape newClip) { + return (newClip == null || newClip.getBounds2D().isEmpty()) ? null : newClip; + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java index 4b1862286b..442884e78c 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java @@ -25,6 +25,7 @@ import static org.apache.poi.hwmf.record.HwmfDraw.readBounds; import static org.apache.poi.hwmf.record.HwmfDraw.readPointS; import java.awt.Shape; +import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Dimension2D; import java.awt.geom.Point2D; @@ -380,7 +381,7 @@ public class HwmfWindowing { * The META_OFFSETCLIPRGN record moves the clipping region in the playback device context by the * specified offsets. */ - public static class WmfOffsetClipRgn implements HwmfRecord, HwmfObjectTableEntry { + public static class WmfOffsetClipRgn implements HwmfRecord { protected final Point2D offset = new Point2D.Double(); @@ -396,11 +397,14 @@ public class HwmfWindowing { @Override public void draw(HwmfGraphics ctx) { - ctx.addObjectTableEntry(this); - } - - @Override - public void applyObject(HwmfGraphics ctx) { + final Shape oldClip = ctx.getProperties().getClip(); + if (oldClip == null) { + return; + } + AffineTransform at = new AffineTransform(); + at.translate(offset.getX(),offset.getY()); + final Shape newClip = at.createTransformedShape(oldClip); + ctx.setClip(newClip, HwmfRegionMode.RGN_COPY, false); } @Override @@ -413,7 +417,7 @@ public class HwmfWindowing { * The META_EXCLUDECLIPRECT record sets the clipping region in the playback device context to the * existing clipping region minus the specified rectangle. */ - public static class WmfExcludeClipRect implements HwmfRecord, HwmfObjectTableEntry { + public static class WmfExcludeClipRect implements HwmfRecord { /** a rectangle in logical units */ protected final Rectangle2D bounds = new Rectangle2D.Double(); @@ -430,14 +434,9 @@ public class HwmfWindowing { @Override public void draw(HwmfGraphics ctx) { - ctx.addObjectTableEntry(this); - } - - @Override - public void applyObject(HwmfGraphics ctx) { ctx.setClip(normalizeBounds(bounds), HwmfRegionMode.RGN_DIFF, false); } - + @Override public String toString() { return boundsToString(bounds); @@ -449,7 +448,7 @@ public class HwmfWindowing { * The META_INTERSECTCLIPRECT record sets the clipping region in the playback device context to the * intersection of the existing clipping region and the specified rectangle. */ - public static class WmfIntersectClipRect implements HwmfRecord, HwmfObjectTableEntry { + public static class WmfIntersectClipRect implements HwmfRecord { /** a rectangle in logical units */ protected final Rectangle2D bounds = new Rectangle2D.Double(); @@ -466,14 +465,9 @@ public class HwmfWindowing { @Override public void draw(HwmfGraphics ctx) { - ctx.addObjectTableEntry(this); + ctx.setClip(bounds, HwmfRegionMode.RGN_AND, false); } - @Override - public void applyObject(HwmfGraphics ctx) { - ctx.setClip(bounds, HwmfRegionMode.RGN_AND, true); - } - @Override public String toString() { return boundsToString(bounds); diff --git a/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java b/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java index da61291066..cb4e92133d 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java @@ -17,17 +17,19 @@ package org.apache.poi.hwmf.usermodel; -import java.awt.Dimension; import java.awt.Graphics2D; +import java.awt.Shape; import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; -import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; +import org.apache.poi.hwmf.draw.HwmfDrawProperties; import org.apache.poi.hwmf.draw.HwmfGraphics; import org.apache.poi.hwmf.record.HwmfHeader; import org.apache.poi.hwmf.record.HwmfPlaceableHeader; @@ -35,6 +37,7 @@ import org.apache.poi.hwmf.record.HwmfRecord; import org.apache.poi.hwmf.record.HwmfRecordType; import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowExt; 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.POILogFactory; @@ -110,7 +113,7 @@ public class HwmfPicture { } public void draw(Graphics2D ctx) { - Dimension dim = getSize(); + Dimension2D dim = getSize(); int width = Units.pointsToPixel(dim.getWidth()); // keep aspect ratio for height int height = Units.pointsToPixel(dim.getHeight()); @@ -119,52 +122,86 @@ public class HwmfPicture { } public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) { - AffineTransform at = ctx.getTransform(); + final Shape clip = ctx.getClip(); + final AffineTransform at = ctx.getTransform(); try { Rectangle2D wmfBounds = getBounds(); + Rectangle2D innerBounds = getInnnerBounds(); + if (innerBounds == null) { + innerBounds = wmfBounds; + } + // scale output bounds to image bounds ctx.translate(graphicsBounds.getX(), graphicsBounds.getY()); ctx.scale(graphicsBounds.getWidth()/wmfBounds.getWidth(), graphicsBounds.getHeight()/wmfBounds.getHeight()); - - HwmfGraphics g = new HwmfGraphics(ctx, wmfBounds); + + ctx.translate(-wmfBounds.getX(), -wmfBounds.getY()); + ctx.translate(innerBounds.getCenterX(), innerBounds.getCenterY()); + ctx.scale(wmfBounds.getWidth()/innerBounds.getWidth(), wmfBounds.getHeight()/innerBounds.getHeight()); + ctx.translate(-wmfBounds.getCenterX(), -wmfBounds.getCenterY()); + + HwmfGraphics g = new HwmfGraphics(ctx, innerBounds); + HwmfDrawProperties prop = g.getProperties(); + prop.setViewportOrg(innerBounds.getX(), innerBounds.getY()); + prop.setViewportExt(innerBounds.getWidth(), innerBounds.getHeight()); + int idx = 0; for (HwmfRecord r : records) { + prop = g.getProperties(); + Shape propClip = prop.getClip(); + Shape ctxClip = ctx.getClip(); + if (!Objects.equals(propClip, ctxClip)) { + int a = 5; + } r.draw(g); idx++; } } finally { ctx.setTransform(at); + ctx.setClip(clip); } } /** * Returns the bounding box in device-independent units. Usually this is taken from the placeable header. - * + * * @return the bounding box + * + * @throws RuntimeException if neither WmfSetWindowOrg/Ext nor the placeableHeader are set */ public Rectangle2D getBounds() { if (placeableHeader != null) { return placeableHeader.getBounds(); - } else { - WmfSetWindowOrg wOrg = null; - WmfSetWindowExt wExt = null; - for (HwmfRecord r : getRecords()) { - if (wOrg != null && wExt != null) { - break; - } - if (r instanceof WmfSetWindowOrg) { - wOrg = (WmfSetWindowOrg)r; - } else if (r instanceof WmfSetWindowExt) { - wExt = (WmfSetWindowExt)r; - } - } - if (wOrg == null || wExt == null) { - throw new RuntimeException("invalid wmf file - window records are incomplete."); + } + Rectangle2D inner = getInnnerBounds(); + if (inner != null) { + return inner; + } + throw new RuntimeException("invalid wmf file - window records are incomplete."); + } + + /** + * Returns the bounding box in device-independent units taken from the WmfSetWindowOrg/Ext records + * + * @return the bounding box or null, if the WmfSetWindowOrg/Ext records aren't set + */ + public Rectangle2D getInnnerBounds() { + WmfSetWindowOrg wOrg = null; + WmfSetWindowExt wExt = null; + for (HwmfRecord r : getRecords()) { + if (r instanceof WmfSetWindowOrg) { + wOrg = (WmfSetWindowOrg)r; + } else if (r instanceof WmfSetWindowExt) { + wExt = (WmfSetWindowExt)r; } - return new Rectangle2D.Double(wOrg.getX(), wOrg.getY(), wExt.getSize().getWidth(), wExt.getSize().getHeight()); - } + if (wOrg != null && wExt != null) { + return new Rectangle2D.Double(wOrg.getX(), wOrg.getY(), wExt.getSize().getWidth(), wExt.getSize().getHeight()); + } + } + return null; } - + + public HwmfPlaceableHeader getPlaceableHeader() { return placeableHeader; } @@ -178,13 +215,13 @@ public class HwmfPicture { * * @return the image size in points */ - public Dimension getSize() { + public Dimension2D getSize() { double inch = (placeableHeader == null) ? 1440 : placeableHeader.getUnitsPerInch(); Rectangle2D bounds = getBounds(); //coefficient to translate from WMF dpi to 72dpi double coeff = Units.POINT_DPI/inch; - return new Dimension((int)Math.round(bounds.getWidth()*coeff), (int)Math.round(bounds.getHeight()*coeff)); + return new Dimension2DDouble(bounds.getWidth()*coeff, bounds.getHeight()*coeff); } public Iterable getEmbeddings() { diff --git a/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java b/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java index 5b0651a93f..64fb8563b3 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java +++ b/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java @@ -20,16 +20,16 @@ package org.apache.poi.hwmf; import static org.apache.poi.POITestCase.assertContains; import static org.junit.Assert.assertEquals; -import javax.imageio.ImageIO; -import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.RenderingHints; +import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; @@ -41,6 +41,8 @@ import java.util.Locale; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import javax.imageio.ImageIO; + import org.apache.poi.POIDataSamples; import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord; import org.apache.poi.hwmf.record.HwmfFont; @@ -84,22 +86,22 @@ public class TestHwmfParsing { @Ignore("This is work-in-progress and not a real unit test ...") public void paint() throws IOException { boolean dumpEmbedded = true; + boolean dumpRecords = false; -// File f = samples.getFile("santa.wmf"); - File f = new File("testme.wmf"); + File f = new File("testme.wmf"); FileInputStream fis = new FileInputStream(f); HwmfPicture wmf = new HwmfPicture(fis); fis.close(); - Dimension dim = wmf.getSize(); - int width = Units.pointsToPixel(dim.getWidth()); + Dimension2D dim = wmf.getSize(); + double width = Units.pointsToPixel(dim.getWidth()); // keep aspect ratio for height - int height = Units.pointsToPixel(dim.getHeight()); + double height = Units.pointsToPixel(dim.getHeight()); double scale = (width > height) ? 1500 / width : 1500 / width; - width *= scale; - height *= scale; + width = Math.abs(width * scale); + height = Math.abs(height * scale); - BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + BufferedImage bufImg = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB); Graphics2D g = bufImg.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); @@ -122,6 +124,17 @@ public class TestHwmfParsing { embIdx++; } } + + if (dumpRecords) { + try (FileWriter fw = new FileWriter("wmf-records.log")) { + for (HwmfRecord r : wmf.getRecords()) { + fw.write(r.getWmfRecordType().name()); + fw.write(":"); + fw.write(r.toString()); + fw.write("\n"); + } + } + } } @Test @@ -198,7 +211,7 @@ public class TestHwmfParsing { } if (renderWmf) { - Dimension dim = wmf.getSize(); + Dimension2D dim = wmf.getSize(); int width = Units.pointsToPixel(dim.getWidth()); // keep aspect ratio for height int height = Units.pointsToPixel(dim.getHeight()); -- 2.39.5