mirror of https://github.com/apache/poi.git
Bug 60656 - EMF image support in slideshows
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1861952 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
9cde034ab9
commit
861988bac3
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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()));
|
||||
|
||||
|
|
|
@ -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<XSLFShape,XSLFTextParagraph> {
|
|||
PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() {
|
||||
@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<XSLFShape,XSLFTextParagraph> {
|
|||
return true;
|
||||
}
|
||||
|
||||
PackagePart pp = shape.getSheet().getPackagePart();
|
||||
PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder);
|
||||
if (paint != null) {
|
||||
setValue(paint);
|
||||
|
|
|
@ -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 =
|
||||
"(?<slideno>[^|]+)\\|(?<format>[^|]+)\\|(?<basename>.+)\\.(?<ext>[^.]++)";
|
||||
|
||||
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] <ppt or pptx file>\n" +
|
||||
(error == null ? "" : ("Error: "+error+"\n")) +
|
||||
"Options:\n" +
|
||||
" -scale <float> scale factor\n" +
|
||||
" -slide <integer> 1-based index of a slide to render\n" +
|
||||
" -format <type> png,gif,jpg (,null for testing)" +
|
||||
" -outdir <dir> output directory, defaults to origin of the ppt/pptx file" +
|
||||
" -quiet do not write to console (for normal processing)";
|
||||
" -scale <float> scale factor\n" +
|
||||
" -slide <integer> 1-based index of a slide to render\n" +
|
||||
" -format <type> png,gif,jpg (,null for testing)\n" +
|
||||
" -outdir <dir> output directory, defaults to origin of the ppt/pptx file\n" +
|
||||
" -outfile <file> output filename, defaults to '"+OUTPUT_PAT_REGEX+"'\n" +
|
||||
" -outpat <pattern> 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();
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<HemfRecord> {
|
|||
}
|
||||
|
||||
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<HemfRecord> {
|
|||
}
|
||||
} finally {
|
||||
ctx.setTransform(at);
|
||||
ctx.setClip(clip);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Shape,Shape,Shape> op;
|
||||
|
||||
HwmfRegionMode(int flag, BiFunction<Shape,Shape,Shape> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
return new Rectangle2D.Double(wOrg.getX(), wOrg.getY(), wExt.getSize().getWidth(), wExt.getSize().getHeight());
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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<HwmfEmbedded> getEmbeddings() {
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue