mirror of https://github.com/apache/poi.git
Regression fixes for H/XSLF and HWMF
see http://apache-poi.1045710.n5.nabble.com/3-14-beta-2-3-14-final-tt5721829.html git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1732236 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
784bdfe20f
commit
c6fa344c54
|
@ -26,10 +26,8 @@ import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.poi.sl.draw.Drawable;
|
import org.apache.poi.sl.draw.DrawFactory;
|
||||||
import org.apache.poi.sl.usermodel.PictureData;
|
import org.apache.poi.sl.usermodel.PictureData;
|
||||||
import org.apache.poi.sl.usermodel.Shape;
|
import org.apache.poi.sl.usermodel.Shape;
|
||||||
import org.apache.poi.sl.usermodel.ShapeContainer;
|
import org.apache.poi.sl.usermodel.ShapeContainer;
|
||||||
|
@ -39,7 +37,6 @@ import org.apache.poi.sl.usermodel.SlideShowFactory;
|
||||||
import org.apache.poi.sl.usermodel.TextParagraph;
|
import org.apache.poi.sl.usermodel.TextParagraph;
|
||||||
import org.apache.poi.sl.usermodel.TextRun;
|
import org.apache.poi.sl.usermodel.TextRun;
|
||||||
import org.apache.poi.sl.usermodel.TextShape;
|
import org.apache.poi.sl.usermodel.TextShape;
|
||||||
import org.apache.poi.util.JvmBugs;
|
|
||||||
|
|
||||||
public abstract class SlideShowHandler extends POIFSFileHandler {
|
public abstract class SlideShowHandler extends POIFSFileHandler {
|
||||||
public void handleSlideShow(SlideShow<?,?> ss) throws IOException {
|
public void handleSlideShow(SlideShow<?,?> ss) throws IOException {
|
||||||
|
@ -55,10 +52,12 @@ public abstract class SlideShowHandler extends POIFSFileHandler {
|
||||||
|
|
||||||
// read in the writen file
|
// read in the writen file
|
||||||
SlideShow<?,?> read = SlideShowFactory.create(new ByteArrayInputStream(out.toByteArray()));
|
SlideShow<?,?> read = SlideShowFactory.create(new ByteArrayInputStream(out.toByteArray()));
|
||||||
|
try {
|
||||||
assertNotNull(read);
|
assertNotNull(read);
|
||||||
|
|
||||||
readContent(read);
|
readContent(read);
|
||||||
|
} finally {
|
||||||
|
read.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ByteArrayOutputStream writeToArray(SlideShow<?,?> ss) throws IOException {
|
private ByteArrayOutputStream writeToArray(SlideShow<?,?> ss) throws IOException {
|
||||||
|
@ -109,7 +108,7 @@ public abstract class SlideShowHandler extends POIFSFileHandler {
|
||||||
for (Slide<?,?> s : ss.getSlides()) {
|
for (Slide<?,?> s : ss.getSlides()) {
|
||||||
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_ARGB);
|
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_ARGB);
|
||||||
Graphics2D graphics = img.createGraphics();
|
Graphics2D graphics = img.createGraphics();
|
||||||
fixFonts(graphics);
|
DrawFactory.getInstance(graphics).fixFonts(graphics);
|
||||||
|
|
||||||
// default rendering options
|
// default rendering options
|
||||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
@ -124,15 +123,4 @@ public abstract class SlideShowHandler extends POIFSFileHandler {
|
||||||
img.flush();
|
img.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static void fixFonts(Graphics2D graphics) {
|
|
||||||
if (!JvmBugs.hasLineBreakMeasurerBug()) return;
|
|
||||||
Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);
|
|
||||||
if (fontMap == null) fontMap = new HashMap<String,String>();
|
|
||||||
fontMap.put("Calibri", "Lucida Sans");
|
|
||||||
fontMap.put("Cambria", "Lucida Bright");
|
|
||||||
graphics.setRenderingHint(Drawable.FONT_MAP, fontMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -43,7 +43,7 @@ public final class EscherArrayProperty extends EscherComplexProperty implements
|
||||||
private boolean sizeIncludesHeaderSize = true;
|
private boolean sizeIncludesHeaderSize = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When reading a property from data stream remeber if the complex part is empty and set this flag.
|
* When reading a property from data stream remember if the complex part is empty and set this flag.
|
||||||
*/
|
*/
|
||||||
private boolean emptyComplexPart = false;
|
private boolean emptyComplexPart = false;
|
||||||
|
|
||||||
|
@ -65,10 +65,7 @@ public final class EscherArrayProperty extends EscherComplexProperty implements
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumberOfElementsInArray() {
|
public int getNumberOfElementsInArray() {
|
||||||
if (emptyComplexPart){
|
return (emptyComplexPart) ? 0 : LittleEndian.getUShort(_complexData, 0);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return LittleEndian.getUShort(_complexData, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNumberOfElementsInArray(int numberOfElements) {
|
public void setNumberOfElementsInArray(int numberOfElements) {
|
||||||
|
@ -82,7 +79,7 @@ public final class EscherArrayProperty extends EscherComplexProperty implements
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumberOfElementsInMemory() {
|
public int getNumberOfElementsInMemory() {
|
||||||
return LittleEndian.getUShort(_complexData, 2);
|
return (emptyComplexPart) ? 0 : LittleEndian.getUShort(_complexData, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNumberOfElementsInMemory(int numberOfElements) {
|
public void setNumberOfElementsInMemory(int numberOfElements) {
|
||||||
|
@ -96,7 +93,7 @@ public final class EscherArrayProperty extends EscherComplexProperty implements
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getSizeOfElements() {
|
public short getSizeOfElements() {
|
||||||
return LittleEndian.getShort( _complexData, 4 );
|
return (emptyComplexPart) ? 0 : LittleEndian.getShort( _complexData, 4 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSizeOfElements(int sizeOfElements) {
|
public void setSizeOfElements(int sizeOfElements) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.awt.geom.Rectangle2D;
|
||||||
import org.apache.poi.sl.usermodel.Background;
|
import org.apache.poi.sl.usermodel.Background;
|
||||||
import org.apache.poi.sl.usermodel.PlaceableShape;
|
import org.apache.poi.sl.usermodel.PlaceableShape;
|
||||||
import org.apache.poi.sl.usermodel.ShapeContainer;
|
import org.apache.poi.sl.usermodel.ShapeContainer;
|
||||||
|
import org.apache.poi.sl.usermodel.Sheet;
|
||||||
|
|
||||||
|
|
||||||
public class DrawBackground extends DrawShape {
|
public class DrawBackground extends DrawShape {
|
||||||
|
@ -47,6 +48,7 @@ public class DrawBackground extends DrawShape {
|
||||||
public void setFlipVertical(boolean flip) {}
|
public void setFlipVertical(boolean flip) {}
|
||||||
public boolean getFlipHorizontal() { return false; }
|
public boolean getFlipHorizontal() { return false; }
|
||||||
public boolean getFlipVertical() { return false; }
|
public boolean getFlipVertical() { return false; }
|
||||||
|
public Sheet<?,?> getSheet() { return shape.getSheet(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
DrawFactory drawFact = DrawFactory.getInstance(graphics);
|
DrawFactory drawFact = DrawFactory.getInstance(graphics);
|
||||||
|
@ -55,6 +57,7 @@ public class DrawBackground extends DrawShape {
|
||||||
Rectangle2D anchor2 = getAnchor(graphics, anchor);
|
Rectangle2D anchor2 = getAnchor(graphics, anchor);
|
||||||
|
|
||||||
if(fill != null) {
|
if(fill != null) {
|
||||||
|
graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, anchor);
|
||||||
graphics.setPaint(fill);
|
graphics.setPaint(fill);
|
||||||
graphics.fill(anchor2);
|
graphics.fill(anchor2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
|
|
||||||
package org.apache.poi.sl.draw;
|
package org.apache.poi.sl.draw;
|
||||||
|
|
||||||
import static org.apache.poi.sl.draw.Drawable.DRAW_FACTORY;
|
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.font.TextLayout;
|
import java.awt.font.TextLayout;
|
||||||
import java.text.AttributedString;
|
import java.text.AttributedString;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.poi.sl.usermodel.Background;
|
import org.apache.poi.sl.usermodel.Background;
|
||||||
import org.apache.poi.sl.usermodel.ConnectorShape;
|
import org.apache.poi.sl.usermodel.ConnectorShape;
|
||||||
|
@ -38,6 +38,7 @@ import org.apache.poi.sl.usermodel.TableShape;
|
||||||
import org.apache.poi.sl.usermodel.TextBox;
|
import org.apache.poi.sl.usermodel.TextBox;
|
||||||
import org.apache.poi.sl.usermodel.TextParagraph;
|
import org.apache.poi.sl.usermodel.TextParagraph;
|
||||||
import org.apache.poi.sl.usermodel.TextShape;
|
import org.apache.poi.sl.usermodel.TextShape;
|
||||||
|
import org.apache.poi.util.JvmBugs;
|
||||||
|
|
||||||
public class DrawFactory {
|
public class DrawFactory {
|
||||||
protected static final ThreadLocal<DrawFactory> defaultFactory = new ThreadLocal<DrawFactory>();
|
protected static final ThreadLocal<DrawFactory> defaultFactory = new ThreadLocal<DrawFactory>();
|
||||||
|
@ -58,7 +59,7 @@ public class DrawFactory {
|
||||||
DrawFactory factory = null;
|
DrawFactory factory = null;
|
||||||
boolean isHint = false;
|
boolean isHint = false;
|
||||||
if (graphics != null) {
|
if (graphics != null) {
|
||||||
factory = (DrawFactory)graphics.getRenderingHint(DRAW_FACTORY);
|
factory = (DrawFactory)graphics.getRenderingHint(Drawable.DRAW_FACTORY);
|
||||||
isHint = (factory != null);
|
isHint = (factory != null);
|
||||||
}
|
}
|
||||||
// secondly try the thread local default
|
// secondly try the thread local default
|
||||||
|
@ -70,7 +71,7 @@ public class DrawFactory {
|
||||||
factory = new DrawFactory();
|
factory = new DrawFactory();
|
||||||
}
|
}
|
||||||
if (graphics != null && !isHint) {
|
if (graphics != null && !isHint) {
|
||||||
graphics.setRenderingHint(DRAW_FACTORY, factory);
|
graphics.setRenderingHint(Drawable.DRAW_FACTORY, factory);
|
||||||
}
|
}
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
@ -166,4 +167,29 @@ public class DrawFactory {
|
||||||
public DrawPaint getPaint(PlaceableShape<?,?> shape) {
|
public DrawPaint getPaint(PlaceableShape<?,?> shape) {
|
||||||
return new DrawPaint(shape);
|
return new DrawPaint(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace font families for Windows JVM 6, which contains a font rendering error.
|
||||||
|
* This is likely to be removed, when POI upgrades to JDK 7
|
||||||
|
*
|
||||||
|
* @param graphics the graphics context which will contain the font mapping
|
||||||
|
*/
|
||||||
|
public void fixFonts(Graphics2D graphics) {
|
||||||
|
if (!JvmBugs.hasLineBreakMeasurerBug()) return;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);
|
||||||
|
if (fontMap == null) {
|
||||||
|
fontMap = new HashMap<String,String>();
|
||||||
|
graphics.setRenderingHint(Drawable.FONT_MAP, fontMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
String fonts[][] = { { "Calibri", "Lucida Sans" }, { "Cambria", "Lucida Bright" } };
|
||||||
|
|
||||||
|
for (String f[] : fonts) {
|
||||||
|
if (!fontMap.containsKey(f[0])) {
|
||||||
|
fontMap.put(f[0], f[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -133,9 +133,12 @@ public class DrawPaint {
|
||||||
|
|
||||||
ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, fill.getContentType());
|
ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, fill.getContentType());
|
||||||
|
|
||||||
|
try {
|
||||||
try {
|
try {
|
||||||
renderer.loadImage(is, fill.getContentType());
|
renderer.loadImage(is, fill.getContentType());
|
||||||
|
} finally {
|
||||||
is.close();
|
is.close();
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.log(POILogger.ERROR, "Can't load image data - using transparent color", e);
|
LOG.log(POILogger.ERROR, "Can't load image data - using transparent color", e);
|
||||||
return null;
|
return null;
|
||||||
|
@ -278,11 +281,31 @@ public class DrawPaint {
|
||||||
snapToAnchor(p1, anchor);
|
snapToAnchor(p1, anchor);
|
||||||
snapToAnchor(p2, anchor);
|
snapToAnchor(p2, anchor);
|
||||||
|
|
||||||
|
if (p1.equals(p2)) {
|
||||||
|
// gradient paint on the same point throws an exception ... and doesn't make sense
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
float[] fractions = fill.getGradientFractions();
|
float[] fractions = fill.getGradientFractions();
|
||||||
Color[] colors = new Color[fractions.length];
|
Color[] colors = new Color[fractions.length];
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (ColorStyle fc : fill.getGradientColors()) {
|
for (ColorStyle fc : fill.getGradientColors()) {
|
||||||
|
if (fc == null) {
|
||||||
|
// get color of background
|
||||||
|
fc = new ColorStyle() {
|
||||||
|
public int getTint() { return -1; }
|
||||||
|
public int getShade() { return -1; }
|
||||||
|
public int getSatOff() { return -1; }
|
||||||
|
public int getSatMod() { return -1; }
|
||||||
|
public int getLumOff() { return -1; }
|
||||||
|
public int getLumMod() { return -1; }
|
||||||
|
public int getHueOff() { return -1; }
|
||||||
|
public int getHueMod() { return -1; }
|
||||||
|
public Color getColor() { return Color.white; }
|
||||||
|
public int getAlpha() { return 0; }
|
||||||
|
};
|
||||||
|
}
|
||||||
colors[i++] = applyColorTransform(fc);
|
colors[i++] = applyColorTransform(fc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,7 @@ public class DrawPictureShape extends DrawSimpleShape {
|
||||||
renderer.loadImage(data.getData(), data.getContentType());
|
renderer.loadImage(data.getData(), data.getContentType());
|
||||||
renderer.drawImage(graphics, anchor, insets);
|
renderer.drawImage(graphics, anchor, insets);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// TODO: draw specific runtime exception?
|
LOG.log(POILogger.ERROR, "image can't be loaded/rendered.", e);
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,17 +90,23 @@ public class DrawShape implements Drawable {
|
||||||
|
|
||||||
Rectangle2D anchor2 = txs.createTransformedShape(ps.getAnchor()).getBounds2D();
|
Rectangle2D anchor2 = txs.createTransformedShape(ps.getAnchor()).getBounds2D();
|
||||||
|
|
||||||
scaleX = anchor.getWidth() == 0. ? 1.0 : anchor.getWidth() / anchor2.getWidth();
|
scaleX = safeScale(anchor.getWidth(), anchor2.getWidth());
|
||||||
scaleY = anchor.getHeight() == 0. ? 1.0 : anchor.getHeight() / anchor2.getHeight();
|
scaleY = safeScale(anchor.getHeight(), anchor2.getHeight());
|
||||||
} else {
|
} else {
|
||||||
quadrant = 0;
|
quadrant = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// transformation is applied reversed ...
|
// transformation is applied reversed ...
|
||||||
graphics.translate(centerX, centerY);
|
graphics.translate(centerX, centerY);
|
||||||
graphics.rotate(Math.toRadians(rotation-quadrant*90.));
|
double rot = Math.toRadians(rotation-quadrant*90.);
|
||||||
|
if (rot != 0) {
|
||||||
|
graphics.rotate(rot);
|
||||||
|
}
|
||||||
graphics.scale(scaleX, scaleY);
|
graphics.scale(scaleX, scaleY);
|
||||||
graphics.rotate(Math.toRadians(quadrant*90));
|
rot = Math.toRadians(quadrant*90);
|
||||||
|
if (rot != 0) {
|
||||||
|
graphics.rotate(rot);
|
||||||
|
}
|
||||||
graphics.translate(-centerX, -centerY);
|
graphics.translate(-centerX, -centerY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +125,12 @@ public class DrawShape implements Drawable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static double safeScale(double dim1, double dim2) {
|
||||||
|
if (dim1 == 0.) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return (dim2 == 0.) ? 1 : dim1/dim2;
|
||||||
|
}
|
||||||
|
|
||||||
public void draw(Graphics2D graphics) {
|
public void draw(Graphics2D graphics) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import java.awt.Graphics2D;
|
||||||
import java.awt.Paint;
|
import java.awt.Paint;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -179,20 +179,20 @@ public class DrawSimpleShape extends DrawShape {
|
||||||
case STEALTH:
|
case STEALTH:
|
||||||
case ARROW:
|
case ARROW:
|
||||||
p = new Path(false, true);
|
p = new Path(false, true);
|
||||||
GeneralPath arrow = new GeneralPath();
|
Path2D.Double arrow = new Path2D.Double();
|
||||||
arrow.moveTo((float) (-lineWidth * scaleX), (float) (-lineWidth * scaleY / 2));
|
arrow.moveTo((-lineWidth * scaleX), (-lineWidth * scaleY / 2));
|
||||||
arrow.lineTo(0, 0);
|
arrow.lineTo(0, 0);
|
||||||
arrow.lineTo((float) (-lineWidth * scaleX), (float) (lineWidth * scaleY / 2));
|
arrow.lineTo((-lineWidth * scaleX), (lineWidth * scaleY / 2));
|
||||||
tailShape = arrow;
|
tailShape = arrow;
|
||||||
at.translate(x2, y2);
|
at.translate(x2, y2);
|
||||||
at.rotate(alpha);
|
at.rotate(alpha);
|
||||||
break;
|
break;
|
||||||
case TRIANGLE:
|
case TRIANGLE:
|
||||||
p = new Path();
|
p = new Path();
|
||||||
GeneralPath triangle = new GeneralPath();
|
Path2D.Double triangle = new Path2D.Double();
|
||||||
triangle.moveTo((float) (-lineWidth * scaleX), (float) (-lineWidth * scaleY / 2));
|
triangle.moveTo((-lineWidth * scaleX), (-lineWidth * scaleY / 2));
|
||||||
triangle.lineTo(0, 0);
|
triangle.lineTo(0, 0);
|
||||||
triangle.lineTo((float) (-lineWidth * scaleX), (float) (lineWidth * scaleY / 2));
|
triangle.lineTo((-lineWidth * scaleX), (lineWidth * scaleY / 2));
|
||||||
triangle.closePath();
|
triangle.closePath();
|
||||||
tailShape = triangle;
|
tailShape = triangle;
|
||||||
at.translate(x2, y2);
|
at.translate(x2, y2);
|
||||||
|
@ -252,20 +252,20 @@ public class DrawSimpleShape extends DrawShape {
|
||||||
case STEALTH:
|
case STEALTH:
|
||||||
case ARROW:
|
case ARROW:
|
||||||
p = new Path(false, true);
|
p = new Path(false, true);
|
||||||
GeneralPath arrow = new GeneralPath();
|
Path2D.Double arrow = new Path2D.Double();
|
||||||
arrow.moveTo((float) (lineWidth * scaleX), (float) (-lineWidth * scaleY / 2));
|
arrow.moveTo((lineWidth * scaleX), (-lineWidth * scaleY / 2));
|
||||||
arrow.lineTo(0, 0);
|
arrow.lineTo(0, 0);
|
||||||
arrow.lineTo((float) (lineWidth * scaleX), (float) (lineWidth * scaleY / 2));
|
arrow.lineTo((lineWidth * scaleX), (lineWidth * scaleY / 2));
|
||||||
headShape = arrow;
|
headShape = arrow;
|
||||||
at.translate(x1, y1);
|
at.translate(x1, y1);
|
||||||
at.rotate(alpha);
|
at.rotate(alpha);
|
||||||
break;
|
break;
|
||||||
case TRIANGLE:
|
case TRIANGLE:
|
||||||
p = new Path();
|
p = new Path();
|
||||||
GeneralPath triangle = new GeneralPath();
|
Path2D.Double triangle = new Path2D.Double();
|
||||||
triangle.moveTo((float) (lineWidth * scaleX), (float) (-lineWidth * scaleY / 2));
|
triangle.moveTo((lineWidth * scaleX), (-lineWidth * scaleY / 2));
|
||||||
triangle.lineTo(0, 0);
|
triangle.lineTo(0, 0);
|
||||||
triangle.lineTo((float) (lineWidth * scaleX), (float) (lineWidth * scaleY / 2));
|
triangle.lineTo((lineWidth * scaleX), (lineWidth * scaleY / 2));
|
||||||
triangle.closePath();
|
triangle.closePath();
|
||||||
headShape = triangle;
|
headShape = triangle;
|
||||||
at.translate(x1, y1);
|
at.translate(x1, y1);
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.apache.poi.sl.usermodel.Insets2D;
|
||||||
import org.apache.poi.sl.usermodel.PaintStyle;
|
import org.apache.poi.sl.usermodel.PaintStyle;
|
||||||
import org.apache.poi.sl.usermodel.PlaceableShape;
|
import org.apache.poi.sl.usermodel.PlaceableShape;
|
||||||
import org.apache.poi.sl.usermodel.ShapeContainer;
|
import org.apache.poi.sl.usermodel.ShapeContainer;
|
||||||
|
import org.apache.poi.sl.usermodel.Sheet;
|
||||||
import org.apache.poi.sl.usermodel.TextParagraph;
|
import org.apache.poi.sl.usermodel.TextParagraph;
|
||||||
import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle;
|
import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle;
|
||||||
import org.apache.poi.sl.usermodel.TextParagraph.TextAlign;
|
import org.apache.poi.sl.usermodel.TextParagraph.TextAlign;
|
||||||
|
@ -465,6 +466,7 @@ public class DrawTextParagraph implements Drawable {
|
||||||
public void setFlipVertical(boolean flip) {}
|
public void setFlipVertical(boolean flip) {}
|
||||||
public boolean getFlipHorizontal() { return false; }
|
public boolean getFlipHorizontal() { return false; }
|
||||||
public boolean getFlipVertical() { return false; }
|
public boolean getFlipVertical() { return false; }
|
||||||
|
public Sheet<?,?> getSheet() { return paragraph.getParentShape().getSheet(); }
|
||||||
};
|
};
|
||||||
return ps;
|
return ps;
|
||||||
}
|
}
|
||||||
|
@ -530,7 +532,7 @@ public class DrawTextParagraph implements Drawable {
|
||||||
attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex));
|
attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
Hyperlink hl = run.getHyperlink();
|
Hyperlink<?,?> hl = run.getHyperlink();
|
||||||
if (hl != null) {
|
if (hl != null) {
|
||||||
attList.add(new AttributedStringData(HYPERLINK_HREF, hl.getAddress(), beginIndex, endIndex));
|
attList.add(new AttributedStringData(HYPERLINK_HREF, hl.getAddress(), beginIndex, endIndex));
|
||||||
attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), beginIndex, endIndex));
|
attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), beginIndex, endIndex));
|
||||||
|
|
|
@ -21,11 +21,15 @@ import java.awt.Graphics2D;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.*;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import org.apache.poi.sl.usermodel.*;
|
import org.apache.poi.sl.usermodel.Insets2D;
|
||||||
|
import org.apache.poi.sl.usermodel.PlaceableShape;
|
||||||
|
import org.apache.poi.sl.usermodel.ShapeContainer;
|
||||||
|
import org.apache.poi.sl.usermodel.TextParagraph;
|
||||||
import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle;
|
import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle;
|
||||||
import org.apache.poi.util.JvmBugs;
|
import org.apache.poi.sl.usermodel.TextRun;
|
||||||
|
import org.apache.poi.sl.usermodel.TextShape;
|
||||||
|
|
||||||
public class DrawTextShape extends DrawSimpleShape {
|
public class DrawTextShape extends DrawSimpleShape {
|
||||||
|
|
||||||
|
@ -35,7 +39,7 @@ public class DrawTextShape extends DrawSimpleShape {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawContent(Graphics2D graphics) {
|
public void drawContent(Graphics2D graphics) {
|
||||||
fixFonts(graphics);
|
DrawFactory.getInstance(graphics).fixFonts(graphics);
|
||||||
|
|
||||||
TextShape<?,?> s = getShape();
|
TextShape<?,?> s = getShape();
|
||||||
|
|
||||||
|
@ -71,7 +75,7 @@ public class DrawTextShape extends DrawSimpleShape {
|
||||||
}
|
}
|
||||||
|
|
||||||
Double textRot = s.getTextRotation();
|
Double textRot = s.getTextRotation();
|
||||||
if (textRot != null) {
|
if (textRot != null && textRot != 0) {
|
||||||
graphics.translate(anchor.getCenterX(), anchor.getCenterY());
|
graphics.translate(anchor.getCenterX(), anchor.getCenterY());
|
||||||
graphics.rotate(Math.toRadians(textRot));
|
graphics.rotate(Math.toRadians(textRot));
|
||||||
graphics.translate(-anchor.getCenterX(), -anchor.getCenterY());
|
graphics.translate(-anchor.getCenterX(), -anchor.getCenterY());
|
||||||
|
@ -110,7 +114,8 @@ public class DrawTextShape extends DrawSimpleShape {
|
||||||
|
|
||||||
double y0 = y;
|
double y0 = y;
|
||||||
//noinspection RedundantCast
|
//noinspection RedundantCast
|
||||||
Iterator<? extends TextParagraph<?,?,? extends TextRun>> paragraphs = (Iterator<? extends TextParagraph<?, ?, ? extends TextRun>>) getShape().iterator();
|
Iterator<? extends TextParagraph<?,?,? extends TextRun>> paragraphs =
|
||||||
|
(Iterator<? extends TextParagraph<?,?,? extends TextRun>>) getShape().iterator();
|
||||||
|
|
||||||
boolean isFirstLine = true;
|
boolean isFirstLine = true;
|
||||||
for (int autoNbrIdx=0; paragraphs.hasNext(); autoNbrIdx++){
|
for (int autoNbrIdx=0; paragraphs.hasNext(); autoNbrIdx++){
|
||||||
|
@ -170,23 +175,10 @@ public class DrawTextShape extends DrawSimpleShape {
|
||||||
// dry-run in a 1x1 image and return the vertical advance
|
// dry-run in a 1x1 image and return the vertical advance
|
||||||
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
|
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
|
||||||
Graphics2D graphics = img.createGraphics();
|
Graphics2D graphics = img.createGraphics();
|
||||||
fixFonts(graphics);
|
DrawFactory.getInstance(graphics).fixFonts(graphics);
|
||||||
return drawParagraphs(graphics, 0, 0);
|
return drawParagraphs(graphics, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static void fixFonts(Graphics2D graphics) {
|
|
||||||
if (!JvmBugs.hasLineBreakMeasurerBug()) return;
|
|
||||||
Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);
|
|
||||||
if (fontMap == null) {
|
|
||||||
fontMap = new HashMap<String,String>();
|
|
||||||
graphics.setRenderingHint(Drawable.FONT_MAP, fontMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fontMap.containsKey("Calibri")) fontMap.put("Calibri", "Lucida Sans");
|
|
||||||
if (!fontMap.containsKey("Cambria")) fontMap.put("Cambria", "Lucida Bright");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TextShape<?,?> getShape() {
|
protected TextShape<?,?> getShape() {
|
||||||
return (TextShape<?,?>)shape;
|
return (TextShape<?,?>)shape;
|
||||||
|
|
|
@ -87,7 +87,7 @@ class PathGradientPaint implements Paint {
|
||||||
) {
|
) {
|
||||||
shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE);
|
shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE);
|
||||||
if (shape == null) {
|
if (shape == null) {
|
||||||
throw new IllegalPathStateException("PathGradientPaint needs a shape to be set via the rendering hint PathGradientPaint.GRADIANT_SHAPE.");
|
throw new IllegalPathStateException("PathGradientPaint needs a shape to be set via the rendering hint Drawable.GRADIANT_SHAPE.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.deviceBounds = deviceBounds;
|
this.deviceBounds = deviceBounds;
|
||||||
|
@ -137,14 +137,14 @@ class PathGradientPaint implements Paint {
|
||||||
return childRaster;
|
return childRaster;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getGradientSteps(Shape shape) {
|
protected int getGradientSteps(Shape gradientShape) {
|
||||||
Rectangle rect = shape.getBounds();
|
Rectangle rect = gradientShape.getBounds();
|
||||||
int lower = 1;
|
int lower = 1;
|
||||||
int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0);
|
int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0);
|
||||||
while (lower < upper-1) {
|
while (lower < upper-1) {
|
||||||
int mid = lower + (upper - lower) / 2;
|
int mid = lower + (upper - lower) / 2;
|
||||||
BasicStroke bs = new BasicStroke(mid, capStyle, joinStyle);
|
BasicStroke bs = new BasicStroke(mid, capStyle, joinStyle);
|
||||||
Area area = new Area(bs.createStrokedShape(shape));
|
Area area = new Area(bs.createStrokedShape(gradientShape));
|
||||||
if (area.isSingular()) {
|
if (area.isSingular()) {
|
||||||
upper = mid;
|
upper = mid;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -42,6 +42,7 @@ import java.awt.geom.Arc2D;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.GeneralPath;
|
||||||
import java.awt.geom.Line2D;
|
import java.awt.geom.Line2D;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.BufferedImageOp;
|
import java.awt.image.BufferedImageOp;
|
||||||
|
@ -243,7 +244,7 @@ public final class SLGraphics extends Graphics2D implements Cloneable {
|
||||||
* @see #setComposite
|
* @see #setComposite
|
||||||
*/
|
*/
|
||||||
public void draw(Shape shape){
|
public void draw(Shape shape){
|
||||||
GeneralPath path = new GeneralPath(_transform.createTransformedShape(shape));
|
Path2D.Double path = new Path2D.Double(_transform.createTransformedShape(shape));
|
||||||
FreeformShape<?,?> p = _group.createFreeform();
|
FreeformShape<?,?> p = _group.createFreeform();
|
||||||
p.setPath(path);
|
p.setPath(path);
|
||||||
p.setFillColor(null);
|
p.setFillColor(null);
|
||||||
|
@ -339,7 +340,7 @@ public final class SLGraphics extends Graphics2D implements Cloneable {
|
||||||
* @see #setClip
|
* @see #setClip
|
||||||
*/
|
*/
|
||||||
public void fill(Shape shape){
|
public void fill(Shape shape){
|
||||||
GeneralPath path = new GeneralPath(_transform.createTransformedShape(shape));
|
Path2D.Double path = new Path2D.Double(_transform.createTransformedShape(shape));
|
||||||
FreeformShape<?,?> p = _group.createFreeform();
|
FreeformShape<?,?> p = _group.createFreeform();
|
||||||
p.setPath(path);
|
p.setPath(path);
|
||||||
applyPaint(p);
|
applyPaint(p);
|
||||||
|
@ -450,7 +451,7 @@ public final class SLGraphics extends Graphics2D implements Cloneable {
|
||||||
*/
|
*/
|
||||||
public void drawRoundRect(int x, int y, int width, int height,
|
public void drawRoundRect(int x, int y, int width, int height,
|
||||||
int arcWidth, int arcHeight){
|
int arcWidth, int arcHeight){
|
||||||
RoundRectangle2D rect = new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight);
|
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight);
|
||||||
draw(rect);
|
draw(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,7 +482,7 @@ public final class SLGraphics extends Graphics2D implements Cloneable {
|
||||||
* @see java.awt.Graphics#drawOval
|
* @see java.awt.Graphics#drawOval
|
||||||
*/
|
*/
|
||||||
public void fillOval(int x, int y, int width, int height){
|
public void fillOval(int x, int y, int width, int height){
|
||||||
Ellipse2D oval = new Ellipse2D.Float(x, y, width, height);
|
Ellipse2D oval = new Ellipse2D.Double(x, y, width, height);
|
||||||
fill(oval);
|
fill(oval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,7 +505,7 @@ public final class SLGraphics extends Graphics2D implements Cloneable {
|
||||||
public void fillRoundRect(int x, int y, int width, int height,
|
public void fillRoundRect(int x, int y, int width, int height,
|
||||||
int arcWidth, int arcHeight){
|
int arcWidth, int arcHeight){
|
||||||
|
|
||||||
RoundRectangle2D rect = new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight);
|
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight);
|
||||||
fill(rect);
|
fill(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,9 +545,8 @@ public final class SLGraphics extends Graphics2D implements Cloneable {
|
||||||
* relative to the start angle.
|
* relative to the start angle.
|
||||||
* @see java.awt.Graphics#drawArc
|
* @see java.awt.Graphics#drawArc
|
||||||
*/
|
*/
|
||||||
public void fillArc(int x, int y, int width, int height,
|
public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle){
|
||||||
int startAngle, int arcAngle){
|
Arc2D arc = new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.PIE);
|
||||||
Arc2D arc = new Arc2D.Float(x, y, width, height, startAngle, arcAngle, Arc2D.PIE);
|
|
||||||
fill(arc);
|
fill(arc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,9 +587,8 @@ public final class SLGraphics extends Graphics2D implements Cloneable {
|
||||||
* relative to the start angle.
|
* relative to the start angle.
|
||||||
* @see java.awt.Graphics#fillArc
|
* @see java.awt.Graphics#fillArc
|
||||||
*/
|
*/
|
||||||
public void drawArc(int x, int y, int width, int height,
|
public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
|
||||||
int startAngle, int arcAngle) {
|
Arc2D arc = new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN);
|
||||||
Arc2D arc = new Arc2D.Float(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN);
|
|
||||||
draw(arc);
|
draw(arc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,7 +635,7 @@ public final class SLGraphics extends Graphics2D implements Cloneable {
|
||||||
* @see java.awt.Graphics#fillOval
|
* @see java.awt.Graphics#fillOval
|
||||||
*/
|
*/
|
||||||
public void drawOval(int x, int y, int width, int height){
|
public void drawOval(int x, int y, int width, int height){
|
||||||
Ellipse2D oval = new Ellipse2D.Float(x, y, width, height);
|
Ellipse2D oval = new Ellipse2D.Double(x, y, width, height);
|
||||||
draw(oval);
|
draw(oval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -932,7 +931,7 @@ public final class SLGraphics extends Graphics2D implements Cloneable {
|
||||||
* @param y2 the second point's <i>y</i> coordinate.
|
* @param y2 the second point's <i>y</i> coordinate.
|
||||||
*/
|
*/
|
||||||
public void drawLine(int x1, int y1, int x2, int y2){
|
public void drawLine(int x1, int y1, int x2, int y2){
|
||||||
Line2D line = new Line2D.Float(x1, y1, x2, y2);
|
Line2D line = new Line2D.Double(x1, y1, x2, y2);
|
||||||
draw(line);
|
draw(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,12 @@
|
||||||
|
|
||||||
package org.apache.poi.sl.draw.geom;
|
package org.apache.poi.sl.draw.geom;
|
||||||
|
|
||||||
import org.apache.poi.sl.draw.binding.CTPath2DArcTo;
|
|
||||||
|
|
||||||
import java.awt.geom.Arc2D;
|
import java.awt.geom.Arc2D;
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
|
|
||||||
|
import org.apache.poi.sl.draw.binding.CTPath2DArcTo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ArcTo command within a shape path in DrawingML:
|
* ArcTo command within a shape path in DrawingML:
|
||||||
*
|
*
|
||||||
|
@ -48,7 +48,7 @@ public class ArcToCommand implements PathCommand {
|
||||||
swAng = arc.getSwAng().toString();
|
swAng = arc.getSwAng().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void execute(GeneralPath path, Context ctx){
|
public void execute(Path2D.Double path, Context ctx){
|
||||||
double rx = ctx.getValue(wr);
|
double rx = ctx.getValue(wr);
|
||||||
double ry = ctx.getValue(hr);
|
double ry = ctx.getValue(hr);
|
||||||
double start = ctx.getValue(stAng) / 60000;
|
double start = ctx.getValue(stAng) / 60000;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
package org.apache.poi.sl.draw.geom;
|
package org.apache.poi.sl.draw.geom;
|
||||||
|
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.Path2D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date: 10/25/11
|
* Date: 10/25/11
|
||||||
|
@ -31,7 +31,7 @@ public class ClosePathCommand implements PathCommand {
|
||||||
ClosePathCommand(){
|
ClosePathCommand(){
|
||||||
}
|
}
|
||||||
|
|
||||||
public void execute(GeneralPath path, Context ctx){
|
public void execute(Path2D.Double path, Context ctx){
|
||||||
path.closePath();
|
path.closePath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
|
|
||||||
package org.apache.poi.sl.draw.geom;
|
package org.apache.poi.sl.draw.geom;
|
||||||
|
|
||||||
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
|
import java.awt.geom.Path2D;
|
||||||
|
|
||||||
import java.awt.geom.GeneralPath;
|
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date: 10/25/11
|
* Date: 10/25/11
|
||||||
|
@ -40,13 +40,13 @@ public class CurveToCommand implements PathCommand {
|
||||||
arg6 = pt3.getY().toString();
|
arg6 = pt3.getY().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void execute(GeneralPath path, Context ctx){
|
public void execute(Path2D.Double path, Context ctx){
|
||||||
double x1 = ctx.getValue(arg1);
|
double x1 = ctx.getValue(arg1);
|
||||||
double y1 = ctx.getValue(arg2);
|
double y1 = ctx.getValue(arg2);
|
||||||
double x2 = ctx.getValue(arg3);
|
double x2 = ctx.getValue(arg3);
|
||||||
double y2 = ctx.getValue(arg4);
|
double y2 = ctx.getValue(arg4);
|
||||||
double x3 = ctx.getValue(arg5);
|
double x3 = ctx.getValue(arg5);
|
||||||
double y3 = ctx.getValue(arg6);
|
double y3 = ctx.getValue(arg6);
|
||||||
path.curveTo((float)x1, (float)y1, (float)x2, (float)y2, (float)x3, (float)y3);
|
path.curveTo(x1, y1, x2, y2, x3, y3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
|
|
||||||
package org.apache.poi.sl.draw.geom;
|
package org.apache.poi.sl.draw.geom;
|
||||||
|
|
||||||
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
|
import java.awt.geom.Path2D;
|
||||||
|
|
||||||
import java.awt.geom.GeneralPath;
|
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date: 10/25/11
|
* Date: 10/25/11
|
||||||
|
@ -41,9 +41,9 @@ public class LineToCommand implements PathCommand {
|
||||||
arg2 = s2;
|
arg2 = s2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void execute(GeneralPath path, Context ctx){
|
public void execute(Path2D.Double path, Context ctx){
|
||||||
double x = ctx.getValue(arg1);
|
double x = ctx.getValue(arg1);
|
||||||
double y = ctx.getValue(arg2);
|
double y = ctx.getValue(arg2);
|
||||||
path.lineTo((float)x, (float)y);
|
path.lineTo(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
|
|
||||||
package org.apache.poi.sl.draw.geom;
|
package org.apache.poi.sl.draw.geom;
|
||||||
|
|
||||||
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
|
import java.awt.geom.Path2D;
|
||||||
|
|
||||||
import java.awt.geom.GeneralPath;
|
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date: 10/25/11
|
* Date: 10/25/11
|
||||||
|
@ -41,9 +41,9 @@ public class MoveToCommand implements PathCommand {
|
||||||
arg2 = s2;
|
arg2 = s2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void execute(GeneralPath path, Context ctx){
|
public void execute(Path2D.Double path, Context ctx){
|
||||||
double x = ctx.getValue(arg1);
|
double x = ctx.getValue(arg1);
|
||||||
double y = ctx.getValue(arg2);
|
double y = ctx.getValue(arg2);
|
||||||
path.moveTo((float)x, (float)y);
|
path.moveTo(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,19 @@
|
||||||
|
|
||||||
package org.apache.poi.sl.draw.geom;
|
package org.apache.poi.sl.draw.geom;
|
||||||
|
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.Path2D;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.poi.sl.draw.binding.*;
|
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
|
||||||
|
import org.apache.poi.sl.draw.binding.CTPath2D;
|
||||||
|
import org.apache.poi.sl.draw.binding.CTPath2DArcTo;
|
||||||
|
import org.apache.poi.sl.draw.binding.CTPath2DClose;
|
||||||
|
import org.apache.poi.sl.draw.binding.CTPath2DCubicBezierTo;
|
||||||
|
import org.apache.poi.sl.draw.binding.CTPath2DLineTo;
|
||||||
|
import org.apache.poi.sl.draw.binding.CTPath2DMoveTo;
|
||||||
|
import org.apache.poi.sl.draw.binding.CTPath2DQuadBezierTo;
|
||||||
|
import org.apache.poi.sl.draw.binding.STPathFillMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies a creation path consisting of a series of moves, lines and curves
|
* Specifies a creation path consisting of a series of moves, lines and curves
|
||||||
|
@ -90,10 +98,10 @@ public class Path {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the internal represenation to java.awt.GeneralPath
|
* Convert the internal represenation to java.awt.geom.Path2D
|
||||||
*/
|
*/
|
||||||
public GeneralPath getPath(Context ctx) {
|
public Path2D.Double getPath(Context ctx) {
|
||||||
GeneralPath path = new GeneralPath();
|
Path2D.Double path = new Path2D.Double();
|
||||||
for(PathCommand cmd : commands)
|
for(PathCommand cmd : commands)
|
||||||
cmd.execute(path, ctx);
|
cmd.execute(path, ctx);
|
||||||
return path;
|
return path;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
package org.apache.poi.sl.draw.geom;
|
package org.apache.poi.sl.draw.geom;
|
||||||
|
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.Path2D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A path command in DrawingML. One of:
|
* A path command in DrawingML. One of:
|
||||||
|
@ -41,5 +41,5 @@ public interface PathCommand {
|
||||||
* @param path the path to append the result to
|
* @param path the path to append the result to
|
||||||
* @param ctx the context to lookup variables
|
* @param ctx the context to lookup variables
|
||||||
*/
|
*/
|
||||||
void execute(GeneralPath path, Context ctx);
|
void execute(Path2D.Double path, Context ctx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
|
|
||||||
package org.apache.poi.sl.draw.geom;
|
package org.apache.poi.sl.draw.geom;
|
||||||
|
|
||||||
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
|
import java.awt.geom.Path2D;
|
||||||
|
|
||||||
import java.awt.geom.GeneralPath;
|
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date: 10/25/11
|
* Date: 10/25/11
|
||||||
|
@ -38,11 +38,11 @@ public class QuadToCommand implements PathCommand {
|
||||||
arg4 = pt2.getY().toString();
|
arg4 = pt2.getY().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void execute(GeneralPath path, Context ctx){
|
public void execute(Path2D.Double path, Context ctx){
|
||||||
double x1 = ctx.getValue(arg1);
|
double x1 = ctx.getValue(arg1);
|
||||||
double y1 = ctx.getValue(arg2);
|
double y1 = ctx.getValue(arg2);
|
||||||
double x2 = ctx.getValue(arg3);
|
double x2 = ctx.getValue(arg3);
|
||||||
double y2 = ctx.getValue(arg4);
|
double y2 = ctx.getValue(arg4);
|
||||||
path.quadTo((float)x1, (float)y1, (float)x2, (float)y2);
|
path.quadTo(x1, y1, x2, y2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
package org.apache.poi.sl.usermodel;
|
package org.apache.poi.sl.usermodel;
|
||||||
|
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.Path2D;
|
||||||
|
|
||||||
public interface FreeformShape<
|
public interface FreeformShape<
|
||||||
S extends Shape<S,P>,
|
S extends Shape<S,P>,
|
||||||
|
@ -33,7 +33,7 @@ public interface FreeformShape<
|
||||||
*
|
*
|
||||||
* @return the path
|
* @return the path
|
||||||
*/
|
*/
|
||||||
GeneralPath getPath();
|
Path2D.Double getPath();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the shape path
|
* Set the shape path
|
||||||
|
@ -41,5 +41,5 @@ public interface FreeformShape<
|
||||||
* @param path shape outline
|
* @param path shape outline
|
||||||
* @return the number of points written
|
* @return the number of points written
|
||||||
*/
|
*/
|
||||||
int setPath(GeneralPath path);
|
int setPath(Path2D.Double path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,11 @@ public interface PlaceableShape<
|
||||||
> {
|
> {
|
||||||
ShapeContainer<S,P> getParent();
|
ShapeContainer<S,P> getParent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the sheet this shape belongs to
|
||||||
|
*/
|
||||||
|
Sheet<S,P> getSheet();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the position of this shape within the drawing canvas.
|
* @return the position of this shape within the drawing canvas.
|
||||||
* The coordinates are expressed in points
|
* The coordinates are expressed in points
|
||||||
|
|
|
@ -26,7 +26,6 @@ public interface Shape<
|
||||||
ShapeContainer<S,P> getParent();
|
ShapeContainer<S,P> getParent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @return the sheet this shape belongs to
|
* @return the sheet this shape belongs to
|
||||||
*/
|
*/
|
||||||
Sheet<S,P> getSheet();
|
Sheet<S,P> getSheet();
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
package org.apache.poi.xslf.usermodel;
|
package org.apache.poi.xslf.usermodel;
|
||||||
|
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.PathIterator;
|
import java.awt.geom.PathIterator;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ public class XSLFFreeformShape extends XSLFAutoShape
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int setPath(GeneralPath path) {
|
public int setPath(Path2D.Double path) {
|
||||||
CTPath2D ctPath = CTPath2D.Factory.newInstance();
|
CTPath2D ctPath = CTPath2D.Factory.newInstance();
|
||||||
|
|
||||||
Rectangle2D bounds = path.getBounds2D();
|
Rectangle2D bounds = path.getBounds2D();
|
||||||
|
@ -119,8 +119,8 @@ public class XSLFFreeformShape extends XSLFAutoShape
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeneralPath getPath() {
|
public Path2D.Double getPath() {
|
||||||
GeneralPath path = new GeneralPath();
|
Path2D.Double path = new Path2D.Double();
|
||||||
Rectangle2D bounds = getAnchor();
|
Rectangle2D bounds = getAnchor();
|
||||||
|
|
||||||
CTCustomGeometry2D geom = getSpPr().getCustGeom();
|
CTCustomGeometry2D geom = getSpPr().getCustGeom();
|
||||||
|
@ -168,7 +168,7 @@ public class XSLFFreeformShape extends XSLFAutoShape
|
||||||
// The returned path should fit in the bounding rectangle
|
// The returned path should fit in the bounding rectangle
|
||||||
AffineTransform at = new AffineTransform();
|
AffineTransform at = new AffineTransform();
|
||||||
at.translate(bounds.getX(), bounds.getY());
|
at.translate(bounds.getX(), bounds.getY());
|
||||||
return new GeneralPath(at.createTransformedShape(path));
|
return new Path2D.Double(at.createTransformedShape(path));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param shapeId 1-based shapeId
|
* @param shapeId 1-based shapeId
|
||||||
|
|
|
@ -48,11 +48,11 @@ public class XSLFHyperlink implements Hyperlink<XSLFShape,XSLFTextParagraph> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAddress() {
|
public String getAddress() {
|
||||||
if (!_link.isSetId()) {
|
String id = _link.getId();
|
||||||
|
if (id == null || "".equals(id)) {
|
||||||
return _link.getAction();
|
return _link.getAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
String id = _link.getId();
|
|
||||||
URI targetURI = _sheet.getPackagePart().getRelationship(id).getTargetURI();
|
URI targetURI = _sheet.getPackagePart().getRelationship(id).getTargetURI();
|
||||||
|
|
||||||
return targetURI.toASCIIString();
|
return targetURI.toASCIIString();
|
||||||
|
|
|
@ -455,8 +455,15 @@ public class XSLFTextRun implements TextRun {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XSLFHyperlink getHyperlink(){
|
public XSLFHyperlink getHyperlink(){
|
||||||
if(!_r.getRPr().isSetHlinkClick()) return null;
|
CTTextCharacterProperties rPr = _r.getRPr();
|
||||||
return new XSLFHyperlink(_r.getRPr().getHlinkClick(), _p.getParentShape().getSheet());
|
if (rPr == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
CTHyperlink hl = rPr.getHlinkClick();
|
||||||
|
if (hl == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new XSLFHyperlink(hl, _p.getParentShape().getSheet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean fetchCharacterProperty(CharacterPropertyFetcher<?> fetcher){
|
private boolean fetchCharacterProperty(CharacterPropertyFetcher<?> fetcher){
|
||||||
|
|
|
@ -24,18 +24,17 @@ import java.awt.Graphics2D;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import org.apache.poi.sl.draw.Drawable;
|
import org.apache.poi.sl.draw.DrawFactory;
|
||||||
import org.apache.poi.sl.usermodel.Slide;
|
import org.apache.poi.sl.usermodel.Slide;
|
||||||
import org.apache.poi.sl.usermodel.SlideShow;
|
import org.apache.poi.sl.usermodel.SlideShow;
|
||||||
import org.apache.poi.sl.usermodel.SlideShowFactory;
|
import org.apache.poi.sl.usermodel.SlideShowFactory;
|
||||||
import org.apache.poi.util.JvmBugs;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An utility to convert slides of a .pptx slide show to a PNG image
|
* An utility to convert slides of a .pptx slide show to a PNG image
|
||||||
|
@ -65,7 +64,7 @@ public class PPTX2PNG {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int slidenum = -1;
|
String slidenumStr = "-1";
|
||||||
float scale = 1;
|
float scale = 1;
|
||||||
File file = null;
|
File file = null;
|
||||||
String format = "png";
|
String format = "png";
|
||||||
|
@ -77,7 +76,7 @@ public class PPTX2PNG {
|
||||||
if ("-scale".equals(args[i])) {
|
if ("-scale".equals(args[i])) {
|
||||||
scale = Float.parseFloat(args[++i]);
|
scale = Float.parseFloat(args[++i]);
|
||||||
} else if ("-slide".equals(args[i])) {
|
} else if ("-slide".equals(args[i])) {
|
||||||
slidenum = Integer.parseInt(args[++i]);
|
slidenumStr = args[++i];
|
||||||
} else if ("-format".equals(args[i])) {
|
} else if ("-format".equals(args[i])) {
|
||||||
format = args[++i];
|
format = args[++i];
|
||||||
} else if ("-outdir".equals(args[i])) {
|
} else if ("-outdir".equals(args[i])) {
|
||||||
|
@ -120,9 +119,11 @@ public class PPTX2PNG {
|
||||||
SlideShow<?,?> ss = SlideShowFactory.create(file, null, true);
|
SlideShow<?,?> ss = SlideShowFactory.create(file, null, true);
|
||||||
List<? extends Slide<?,?>> slides = ss.getSlides();
|
List<? extends Slide<?,?>> slides = ss.getSlides();
|
||||||
|
|
||||||
|
Set<Integer> slidenum = slideIndexes(slides.size(), slidenumStr);
|
||||||
|
|
||||||
if (slidenum < -1 || slidenum == 0 || slidenum > slides.size()) {
|
if (slidenum.isEmpty()) {
|
||||||
usage("slidenum must be either -1 (for all) or within range: [1.."+slides.size()+"] for "+file);
|
usage("slidenum must be either -1 (for all) or within range: [1.."+slides.size()+"] for "+file);
|
||||||
|
ss.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,9 +131,8 @@ public class PPTX2PNG {
|
||||||
int width = (int) (pgsize.width * scale);
|
int width = (int) (pgsize.width * scale);
|
||||||
int height = (int) (pgsize.height * scale);
|
int height = (int) (pgsize.height * scale);
|
||||||
|
|
||||||
int slideNo=1;
|
for(Integer slideNo : slidenum) {
|
||||||
for(Slide<?,?> slide : slides) {
|
Slide<?,?> slide = slides.get(slideNo);
|
||||||
if (slidenum == -1 || slideNo == slidenum) {
|
|
||||||
String title = slide.getTitle();
|
String title = slide.getTitle();
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
System.out.println("Rendering slide " + slideNo + (title == null ? "" : ": " + title));
|
System.out.println("Rendering slide " + slideNo + (title == null ? "" : ": " + title));
|
||||||
|
@ -140,7 +140,7 @@ public class PPTX2PNG {
|
||||||
|
|
||||||
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||||
Graphics2D graphics = img.createGraphics();
|
Graphics2D graphics = img.createGraphics();
|
||||||
fixFonts(graphics);
|
DrawFactory.getInstance(graphics).fixFonts(graphics);
|
||||||
|
|
||||||
// default rendering options
|
// default rendering options
|
||||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
@ -161,8 +161,6 @@ public class PPTX2PNG {
|
||||||
ImageIO.write(img, format, outfile);
|
ImageIO.write(img, format, outfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slideNo++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
System.out.println("Done");
|
System.out.println("Done");
|
||||||
|
@ -171,13 +169,42 @@ public class PPTX2PNG {
|
||||||
ss.close();
|
ss.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
private static Set<Integer> slideIndexes(final int slideCount, String range) {
|
||||||
private static void fixFonts(Graphics2D graphics) {
|
Set<Integer> slideIdx = new TreeSet<Integer>();
|
||||||
if (!JvmBugs.hasLineBreakMeasurerBug()) return;
|
if ("-1".equals(range)) {
|
||||||
Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);
|
for (int i=0; i<slideCount; i++) {
|
||||||
if (fontMap == null) fontMap = new HashMap<String,String>();
|
slideIdx.add(i);
|
||||||
fontMap.put("Calibri", "Lucida Sans");
|
}
|
||||||
fontMap.put("Cambria", "Lucida Bright");
|
} else {
|
||||||
graphics.setRenderingHint(Drawable.FONT_MAP, fontMap);
|
for (String subrange : range.split(",")) {
|
||||||
|
String idx[] = subrange.split("-");
|
||||||
|
switch (idx.length) {
|
||||||
|
default:
|
||||||
|
case 0: break;
|
||||||
|
case 1: {
|
||||||
|
int subidx = Integer.parseInt(idx[0]);
|
||||||
|
if (subrange.contains("-")) {
|
||||||
|
int startIdx = subrange.startsWith("-") ? 0 : subidx;
|
||||||
|
int endIdx = subrange.endsWith("-") ? slideCount : Math.min(subidx,slideCount);
|
||||||
|
for (int i=Math.max(startIdx,1); i<endIdx; i++) {
|
||||||
|
slideIdx.add(i-1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
slideIdx.add(Math.max(subidx,1)-1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
int startIdx = Math.min(Integer.parseInt(idx[0]), slideCount);
|
||||||
|
int endIdx = Math.min(Integer.parseInt(idx[1]), slideCount);
|
||||||
|
for (int i=Math.max(startIdx,1); i<endIdx; i++) {
|
||||||
|
slideIdx.add(i-1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slideIdx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,17 +36,28 @@ public class TestPPTX2PNG {
|
||||||
public void render() throws Exception {
|
public void render() throws Exception {
|
||||||
POIDataSamples samples = POIDataSamples.getSlideShowInstance();
|
POIDataSamples samples = POIDataSamples.getSlideShowInstance();
|
||||||
|
|
||||||
String[] testFiles = {"alterman_security.ppt","alterman_security.pptx","KEY02.pptx","themes.pptx","backgrounds.pptx","layouts.pptx", "sample.pptx", "shapes.pptx",};
|
// File testFilesX[] = new File("tmp_ppt").listFiles(new FileFilter() {
|
||||||
// String[] testFiles = {"41246-2.ppt","45543.ppt","53446.ppt","ParagraphStylesShorterThanCharStyles.ppt"};
|
// public boolean accept(File pathname) {
|
||||||
|
// return pathname.getName().toLowerCase().contains("ppt");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// String testFiles[] = new String[testFilesX.length];
|
||||||
|
// for (int i=0; i<testFilesX.length; i++) {
|
||||||
|
// testFiles[i] = testFilesX[i].getPath();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
String[] testFiles = {"53446.ppt", "alterman_security.ppt","alterman_security.pptx","KEY02.pptx","themes.pptx","backgrounds.pptx","layouts.pptx", "sample.pptx", "shapes.pptx",};
|
||||||
String[] args = {
|
String[] args = {
|
||||||
"-format", "null", // png,gif,jpg or null for test
|
"-format", "null", // png,gif,jpg or null for test
|
||||||
"-slide", "-1", // -1 for all
|
"-slide", "-1", // -1 for all
|
||||||
"-outdir", new File("build/tmp/").getCanonicalPath(),
|
"-outdir", new File("build/tmp/").getCanonicalPath(),
|
||||||
"-quite",
|
"-quiet",
|
||||||
"dummyfile"
|
"dummyfile"
|
||||||
};
|
};
|
||||||
for(String sampleFile : testFiles){
|
for(String sampleFile : testFiles){
|
||||||
args[args.length-1] = samples.getFile(sampleFile).getCanonicalPath();
|
args[args.length-1] = samples.getFile(sampleFile).getCanonicalPath();
|
||||||
|
// args[args.length-1] = new File(sampleFile).getCanonicalPath();
|
||||||
try {
|
try {
|
||||||
PPTX2PNG.main(args);
|
PPTX2PNG.main(args);
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
|
|
|
@ -19,8 +19,9 @@ package org.apache.poi.xslf.usermodel;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -30,16 +31,16 @@ import org.junit.Test;
|
||||||
public class TestXSLFFreeformShape {
|
public class TestXSLFFreeformShape {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetPath() {
|
public void testSetPath() throws IOException {
|
||||||
XMLSlideShow ppt = new XMLSlideShow();
|
XMLSlideShow ppt = new XMLSlideShow();
|
||||||
XSLFSlide slide = ppt.createSlide();
|
XSLFSlide slide = ppt.createSlide();
|
||||||
XSLFFreeformShape shape1 = slide.createFreeform();
|
XSLFFreeformShape shape1 = slide.createFreeform();
|
||||||
// comples path consisting of a rectangle and an ellipse inside it
|
// comples path consisting of a rectangle and an ellipse inside it
|
||||||
GeneralPath path1 = new GeneralPath(new Rectangle2D.Double(150, 150, 300, 300));
|
Path2D.Double path1 = new Path2D.Double(new Rectangle2D.Double(150, 150, 300, 300));
|
||||||
path1.append(new Ellipse2D.Double(200, 200, 100, 50), false);
|
path1.append(new Ellipse2D.Double(200, 200, 100, 50), false);
|
||||||
shape1.setPath(path1);
|
shape1.setPath(path1);
|
||||||
|
|
||||||
GeneralPath path2 = shape1.getPath();
|
Path2D.Double path2 = shape1.getPath();
|
||||||
|
|
||||||
// YK: how to compare the original path1 and the value returned by XSLFFreeformShape.getPath() ?
|
// YK: how to compare the original path1 and the value returned by XSLFFreeformShape.getPath() ?
|
||||||
// one way is to create another XSLFFreeformShape from path2 and compare the resulting xml
|
// one way is to create another XSLFFreeformShape from path2 and compare the resulting xml
|
||||||
|
@ -49,5 +50,7 @@ public class TestXSLFFreeformShape {
|
||||||
shape2.setPath(path2);
|
shape2.setPath(path2);
|
||||||
|
|
||||||
assertEquals(shape1.getSpPr().getCustGeom().toString(), shape2.getSpPr().getCustGeom().toString());
|
assertEquals(shape1.getSpPr().getCustGeom().toString(), shape2.getSpPr().getCustGeom().toString());
|
||||||
|
|
||||||
|
ppt.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -42,6 +42,7 @@ import java.awt.geom.Arc2D;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.GeneralPath;
|
||||||
import java.awt.geom.Line2D;
|
import java.awt.geom.Line2D;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.BufferedImageOp;
|
import java.awt.image.BufferedImageOp;
|
||||||
|
@ -244,7 +245,7 @@ public final class PPGraphics2D extends Graphics2D implements Cloneable {
|
||||||
* @see #setComposite
|
* @see #setComposite
|
||||||
*/
|
*/
|
||||||
public void draw(Shape shape){
|
public void draw(Shape shape){
|
||||||
GeneralPath path = new GeneralPath(_transform.createTransformedShape(shape));
|
Path2D.Double path = new Path2D.Double(_transform.createTransformedShape(shape));
|
||||||
HSLFFreeformShape p = new HSLFFreeformShape(_group);
|
HSLFFreeformShape p = new HSLFFreeformShape(_group);
|
||||||
p.setPath(path);
|
p.setPath(path);
|
||||||
p.getFill().setForegroundColor(null);
|
p.getFill().setForegroundColor(null);
|
||||||
|
@ -346,7 +347,7 @@ public final class PPGraphics2D extends Graphics2D implements Cloneable {
|
||||||
* @see #setClip
|
* @see #setClip
|
||||||
*/
|
*/
|
||||||
public void fill(Shape shape){
|
public void fill(Shape shape){
|
||||||
GeneralPath path = new GeneralPath(_transform.createTransformedShape(shape));
|
Path2D.Double path = new Path2D.Double(_transform.createTransformedShape(shape));
|
||||||
HSLFFreeformShape p = new HSLFFreeformShape(_group);
|
HSLFFreeformShape p = new HSLFFreeformShape(_group);
|
||||||
p.setPath(path);
|
p.setPath(path);
|
||||||
applyPaint(p);
|
applyPaint(p);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents the metadata of a link in a slide/notes/etc.
|
* This class represents the metadata of a link in a slide/notes/etc.
|
||||||
|
@ -59,11 +60,12 @@ public class InteractiveInfo extends RecordContainer {
|
||||||
*/
|
*/
|
||||||
private void findInterestingChildren() {
|
private void findInterestingChildren() {
|
||||||
// First child should be the InteractiveInfoAtom
|
// First child should be the InteractiveInfoAtom
|
||||||
if(_children[0] instanceof InteractiveInfoAtom) {
|
if (_children == null || _children.length == 0 || !(_children[0] instanceof InteractiveInfoAtom)) {
|
||||||
infoAtom = (InteractiveInfoAtom)_children[0];
|
logger.log(POILogger.WARN, "First child record wasn't a InteractiveInfoAtom - leaving this atom in an invalid state...");
|
||||||
} else {
|
return;
|
||||||
throw new IllegalStateException("First child record wasn't a InteractiveInfoAtom, was of type " + _children[0].getRecordType());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
infoAtom = (InteractiveInfoAtom)_children[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -168,7 +168,7 @@ public abstract class Record
|
||||||
try {
|
try {
|
||||||
c = RecordTypes.forTypeID((short)type).handlingClass;
|
c = RecordTypes.forTypeID((short)type).handlingClass;
|
||||||
if(c == null) {
|
if(c == null) {
|
||||||
// How odd. RecordTypes normally subsitutes in
|
// How odd. RecordTypes normally substitutes in
|
||||||
// a default handler class if it has heard of the record
|
// a default handler class if it has heard of the record
|
||||||
// type but there's no support for it. Explicitly request
|
// type but there's no support for it. Explicitly request
|
||||||
// that now
|
// that now
|
||||||
|
|
|
@ -23,23 +23,28 @@ import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.poi.ddf.AbstractEscherOptRecord;
|
import org.apache.poi.ddf.AbstractEscherOptRecord;
|
||||||
|
import org.apache.poi.ddf.EscherArrayProperty;
|
||||||
import org.apache.poi.ddf.EscherBSERecord;
|
import org.apache.poi.ddf.EscherBSERecord;
|
||||||
|
import org.apache.poi.ddf.EscherColorRef;
|
||||||
import org.apache.poi.ddf.EscherContainerRecord;
|
import org.apache.poi.ddf.EscherContainerRecord;
|
||||||
import org.apache.poi.ddf.EscherProperties;
|
import org.apache.poi.ddf.EscherProperties;
|
||||||
import org.apache.poi.ddf.EscherRecord;
|
import org.apache.poi.ddf.EscherRecord;
|
||||||
import org.apache.poi.ddf.EscherSimpleProperty;
|
import org.apache.poi.ddf.EscherSimpleProperty;
|
||||||
import org.apache.poi.hslf.record.Document;
|
import org.apache.poi.hslf.record.Document;
|
||||||
import org.apache.poi.sl.draw.DrawPaint;
|
import org.apache.poi.sl.draw.DrawPaint;
|
||||||
|
import org.apache.poi.sl.usermodel.ColorStyle;
|
||||||
import org.apache.poi.sl.usermodel.FillStyle;
|
import org.apache.poi.sl.usermodel.FillStyle;
|
||||||
import org.apache.poi.sl.usermodel.PaintStyle;
|
import org.apache.poi.sl.usermodel.PaintStyle;
|
||||||
|
import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint;
|
||||||
|
import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint.GradientType;
|
||||||
import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint;
|
import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint;
|
||||||
|
import org.apache.poi.util.LittleEndian;
|
||||||
import org.apache.poi.util.POILogFactory;
|
import org.apache.poi.util.POILogFactory;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
|
import org.apache.poi.util.Units;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents functionality provided by the 'Fill Effects' dialog in PowerPoint.
|
* Represents functionality provided by the 'Fill Effects' dialog in PowerPoint.
|
||||||
*
|
|
||||||
* @author Yegor Kozlov
|
|
||||||
*/
|
*/
|
||||||
public final class HSLFFill {
|
public final class HSLFFill {
|
||||||
// For logging
|
// For logging
|
||||||
|
@ -118,12 +123,94 @@ public final class HSLFFill {
|
||||||
public FillStyle getFillStyle() {
|
public FillStyle getFillStyle() {
|
||||||
return new FillStyle() {
|
return new FillStyle() {
|
||||||
public PaintStyle getPaint() {
|
public PaintStyle getPaint() {
|
||||||
switch (getFillType()) {
|
final int fillType = getFillType();
|
||||||
|
// TODO: fix gradient types, this mismatches with the MS-ODRAW definition ...
|
||||||
|
// need to handle (not only) the type (radial,rectangular,linear),
|
||||||
|
// the direction, e.g. top right, and bounds (e.g. for rectangular boxes)
|
||||||
|
switch (fillType) {
|
||||||
case FILL_SOLID:
|
case FILL_SOLID:
|
||||||
return DrawPaint.createSolidPaint(getForegroundColor());
|
return DrawPaint.createSolidPaint(getForegroundColor());
|
||||||
case FILL_PICTURE: {
|
case FILL_SHADE_SHAPE:
|
||||||
|
return getGradientPaint(GradientType.shape);
|
||||||
|
case FILL_SHADE_CENTER:
|
||||||
|
case FILL_SHADE_TITLE:
|
||||||
|
return getGradientPaint(GradientType.circular);
|
||||||
|
case FILL_SHADE:
|
||||||
|
case FILL_SHADE_SCALE:
|
||||||
|
return getGradientPaint(GradientType.linear);
|
||||||
|
case FILL_PICTURE:
|
||||||
|
return getTexturePaint();
|
||||||
|
default:
|
||||||
|
logger.log(POILogger.WARN, "unsuported fill type: " + fillType);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private GradientPaint getGradientPaint(final GradientType gradientType) {
|
||||||
|
final AbstractEscherOptRecord opt = shape.getEscherOptRecord();
|
||||||
|
final EscherArrayProperty ep = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__SHADECOLORS);
|
||||||
|
final int colorCnt = (ep == null) ? 0 : ep.getNumberOfElementsInArray();
|
||||||
|
|
||||||
|
return new GradientPaint() {
|
||||||
|
public double getGradientAngle() {
|
||||||
|
// A value of type FixedPoint, as specified in [MS-OSHARED] section 2.2.1.6,
|
||||||
|
// that specifies the angle of the gradient fill. Zero degrees represents a vertical vector from
|
||||||
|
// bottom to top. The default value for this property is 0x00000000.
|
||||||
|
int rot = shape.getEscherProperty(EscherProperties.FILL__ANGLE);
|
||||||
|
return 90-Units.fixedPointToDouble(rot);
|
||||||
|
}
|
||||||
|
public ColorStyle[] getGradientColors() {
|
||||||
|
ColorStyle cs[];
|
||||||
|
if (colorCnt == 0) {
|
||||||
|
cs = new ColorStyle[2];
|
||||||
|
cs[0] = wrapColor(getBackgroundColor());
|
||||||
|
cs[1] = wrapColor(getForegroundColor());
|
||||||
|
} else {
|
||||||
|
cs = new ColorStyle[colorCnt];
|
||||||
|
int idx = 0;
|
||||||
|
// TODO: handle palette colors and alpha(?) value
|
||||||
|
for (byte data[] : ep) {
|
||||||
|
EscherColorRef ecr = new EscherColorRef(data, 0, 4);
|
||||||
|
cs[idx++] = wrapColor(shape.getColor(ecr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
private ColorStyle wrapColor(Color col) {
|
||||||
|
return (col == null) ? null : DrawPaint.createSolidPaint(col).getSolidColor();
|
||||||
|
}
|
||||||
|
public float[] getGradientFractions() {
|
||||||
|
float frc[];
|
||||||
|
if (colorCnt == 0) {
|
||||||
|
frc = new float[]{0, 1};
|
||||||
|
} else {
|
||||||
|
frc = new float[colorCnt];
|
||||||
|
int idx = 0;
|
||||||
|
for (byte data[] : ep) {
|
||||||
|
double pos = Units.fixedPointToDouble(LittleEndian.getInt(data, 4));
|
||||||
|
frc[idx++] = (float)pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return frc;
|
||||||
|
}
|
||||||
|
public boolean isRotatedWithShape() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public GradientType getGradientType() {
|
||||||
|
return gradientType;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private TexturePaint getTexturePaint() {
|
||||||
final HSLFPictureData pd = getPictureData();
|
final HSLFPictureData pd = getPictureData();
|
||||||
if (pd == null) break;
|
if (pd == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return new TexturePaint() {
|
return new TexturePaint() {
|
||||||
public InputStream getImageData() {
|
public InputStream getImageData() {
|
||||||
|
@ -139,14 +226,6 @@ public final class HSLFFill {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
logger.log(POILogger.WARN, "unsuported fill type: " + getFillType());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns fill type.
|
* Returns fill type.
|
||||||
|
@ -172,6 +251,7 @@ public final class HSLFFill {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
protected EscherBSERecord getEscherBSERecord(int idx){
|
protected EscherBSERecord getEscherBSERecord(int idx){
|
||||||
HSLFSheet sheet = shape.getSheet();
|
HSLFSheet sheet = shape.getSheet();
|
||||||
if(sheet == null) {
|
if(sheet == null) {
|
||||||
|
@ -258,6 +338,7 @@ public final class HSLFFill {
|
||||||
/**
|
/**
|
||||||
* <code>PictureData</code> object used in a texture, pattern of picture fill.
|
* <code>PictureData</code> object used in a texture, pattern of picture fill.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("resource")
|
||||||
public HSLFPictureData getPictureData(){
|
public HSLFPictureData getPictureData(){
|
||||||
AbstractEscherOptRecord opt = shape.getEscherOptRecord();
|
AbstractEscherOptRecord opt = shape.getEscherOptRecord();
|
||||||
EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
|
EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
|
||||||
|
|
|
@ -18,22 +18,25 @@
|
||||||
package org.apache.poi.hslf.usermodel;
|
package org.apache.poi.hslf.usermodel;
|
||||||
|
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.PathIterator;
|
import java.awt.geom.PathIterator;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.poi.ddf.AbstractEscherOptRecord;
|
import org.apache.poi.ddf.AbstractEscherOptRecord;
|
||||||
import org.apache.poi.ddf.EscherArrayProperty;
|
import org.apache.poi.ddf.EscherArrayProperty;
|
||||||
import org.apache.poi.ddf.EscherContainerRecord;
|
import org.apache.poi.ddf.EscherContainerRecord;
|
||||||
import org.apache.poi.ddf.EscherProperties;
|
import org.apache.poi.ddf.EscherProperties;
|
||||||
|
import org.apache.poi.ddf.EscherProperty;
|
||||||
import org.apache.poi.ddf.EscherSimpleProperty;
|
import org.apache.poi.ddf.EscherSimpleProperty;
|
||||||
import org.apache.poi.sl.usermodel.FreeformShape;
|
import org.apache.poi.sl.usermodel.FreeformShape;
|
||||||
import org.apache.poi.sl.usermodel.ShapeContainer;
|
import org.apache.poi.sl.usermodel.ShapeContainer;
|
||||||
import org.apache.poi.sl.usermodel.ShapeType;
|
import org.apache.poi.sl.usermodel.ShapeType;
|
||||||
|
import org.apache.poi.util.BitField;
|
||||||
|
import org.apache.poi.util.BitFieldFactory;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
import org.apache.poi.util.Units;
|
import org.apache.poi.util.Units;
|
||||||
|
@ -57,6 +60,85 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
|
||||||
public static final byte[] SEGMENTINFO_CLOSE = new byte[]{0x01, (byte)0x60};
|
public static final byte[] SEGMENTINFO_CLOSE = new byte[]{0x01, (byte)0x60};
|
||||||
public static final byte[] SEGMENTINFO_END = new byte[]{0x00, (byte)0x80};
|
public static final byte[] SEGMENTINFO_END = new byte[]{0x00, (byte)0x80};
|
||||||
|
|
||||||
|
private static BitField PATH_INFO = BitFieldFactory.getInstance(0xE000);
|
||||||
|
private static BitField ESCAPE_INFO = BitFieldFactory.getInstance(0x1F00);
|
||||||
|
|
||||||
|
enum PathInfo {
|
||||||
|
lineTo(0),curveTo(1),moveTo(2),close(3),end(4),escape(5),clientEscape(6);
|
||||||
|
int flag;
|
||||||
|
PathInfo(int flag) {
|
||||||
|
this.flag = flag;
|
||||||
|
}
|
||||||
|
static PathInfo valueOf(int flag) {
|
||||||
|
for (PathInfo v : values()) {
|
||||||
|
if (v.flag == flag) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EscapeInfo {
|
||||||
|
EXTENSION(0x0000),
|
||||||
|
ANGLE_ELLIPSE_TO(0x0001),
|
||||||
|
ANGLE_ELLIPSE(0x0002),
|
||||||
|
ARC_TO(0x0003),
|
||||||
|
ARC(0x0004),
|
||||||
|
CLOCKWISE_ARC_TO(0x0005),
|
||||||
|
CLOCKWISE_ARC(0x0006),
|
||||||
|
ELLIPTICAL_QUADRANT_X(0x0007),
|
||||||
|
ELLIPTICAL_QUADRANT_Y(0x0008),
|
||||||
|
QUADRATIC_BEZIER(0x0009),
|
||||||
|
NO_FILL(0X000A),
|
||||||
|
NO_LINE(0X000B),
|
||||||
|
AUTO_LINE(0X000C),
|
||||||
|
AUTO_CURVE(0X000D),
|
||||||
|
CORNER_LINE(0X000E),
|
||||||
|
CORNER_CURVE(0X000F),
|
||||||
|
SMOOTH_LINE(0X0010),
|
||||||
|
SMOOTH_CURVE(0X0011),
|
||||||
|
SYMMETRIC_LINE(0X0012),
|
||||||
|
SYMMETRIC_CURVE(0X0013),
|
||||||
|
FREEFORM(0X0014),
|
||||||
|
FILL_COLOR(0X0015),
|
||||||
|
LINE_COLOR(0X0016);
|
||||||
|
|
||||||
|
int flag;
|
||||||
|
EscapeInfo(int flag) {
|
||||||
|
this.flag = flag;
|
||||||
|
}
|
||||||
|
static EscapeInfo valueOf(int flag) {
|
||||||
|
for (EscapeInfo v : values()) {
|
||||||
|
if (v.flag == flag) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ShapePath {
|
||||||
|
LINES(0),
|
||||||
|
LINES_CLOSED(1),
|
||||||
|
CURVES(2),
|
||||||
|
CURVES_CLOSED(3),
|
||||||
|
COMPLEX(4);
|
||||||
|
|
||||||
|
int flag;
|
||||||
|
ShapePath(int flag) {
|
||||||
|
this.flag = flag;
|
||||||
|
}
|
||||||
|
static ShapePath valueOf(int flag) {
|
||||||
|
for (ShapePath v : values()) {
|
||||||
|
if (v.flag == flag) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Freeform object and initialize it from the supplied Record container.
|
* Create a Freeform object and initialize it from the supplied Record container.
|
||||||
*
|
*
|
||||||
|
@ -88,7 +170,7 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int setPath(GeneralPath path) {
|
public int setPath(Path2D.Double path) {
|
||||||
Rectangle2D bounds = path.getBounds2D();
|
Rectangle2D bounds = path.getBounds2D();
|
||||||
PathIterator it = path.getPathIterator(new AffineTransform());
|
PathIterator it = path.getPathIterator(new AffineTransform());
|
||||||
|
|
||||||
|
@ -179,18 +261,14 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeneralPath getPath(){
|
public Path2D.Double getPath(){
|
||||||
AbstractEscherOptRecord opt = getEscherOptRecord();
|
AbstractEscherOptRecord opt = getEscherOptRecord();
|
||||||
opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 0x4));
|
|
||||||
|
|
||||||
EscherArrayProperty verticesProp = getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES + 0x4000));
|
EscherArrayProperty verticesProp = getShapeProp(opt, EscherProperties.GEOMETRY__VERTICES);
|
||||||
if(verticesProp == null) verticesProp = getEscherProperty(opt, EscherProperties.GEOMETRY__VERTICES);
|
EscherArrayProperty segmentsProp = getShapeProp(opt, EscherProperties.GEOMETRY__SEGMENTINFO);
|
||||||
|
|
||||||
EscherArrayProperty segmentsProp = getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO + 0x4000));
|
|
||||||
if(segmentsProp == null) segmentsProp = getEscherProperty(opt, EscherProperties.GEOMETRY__SEGMENTINFO);
|
|
||||||
|
|
||||||
// return empty path if either GEOMETRY__VERTICES or GEOMETRY__SEGMENTINFO is missing, see Bugzilla 54188
|
// return empty path if either GEOMETRY__VERTICES or GEOMETRY__SEGMENTINFO is missing, see Bugzilla 54188
|
||||||
GeneralPath path = new GeneralPath();
|
Path2D.Double path = new Path2D.Double();
|
||||||
|
|
||||||
//sanity check
|
//sanity check
|
||||||
if(verticesProp == null) {
|
if(verticesProp == null) {
|
||||||
|
@ -202,45 +280,59 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
int numPoints = verticesProp.getNumberOfElementsInArray();
|
Iterator<byte[]> vertIter = verticesProp.iterator();
|
||||||
int numSegments = segmentsProp.getNumberOfElementsInArray();
|
Iterator<byte[]> segIter = segmentsProp.iterator();
|
||||||
for (int i = 0, j = 0; i < numSegments && j < numPoints; i++) {
|
|
||||||
byte[] elem = segmentsProp.getElement(i);
|
|
||||||
if(Arrays.equals(elem, SEGMENTINFO_MOVETO)){
|
|
||||||
byte[] p = verticesProp.getElement(j++);
|
|
||||||
short x = LittleEndian.getShort(p, 0);
|
|
||||||
short y = LittleEndian.getShort(p, 2);
|
|
||||||
path.moveTo(Units.masterToPoints(x), Units.masterToPoints(y));
|
|
||||||
} else if (Arrays.equals(elem, SEGMENTINFO_CUBICTO) || Arrays.equals(elem, SEGMENTINFO_CUBICTO2)){
|
|
||||||
i++;
|
|
||||||
byte[] p1 = verticesProp.getElement(j++);
|
|
||||||
short x1 = LittleEndian.getShort(p1, 0);
|
|
||||||
short y1 = LittleEndian.getShort(p1, 2);
|
|
||||||
byte[] p2 = verticesProp.getElement(j++);
|
|
||||||
short x2 = LittleEndian.getShort(p2, 0);
|
|
||||||
short y2 = LittleEndian.getShort(p2, 2);
|
|
||||||
byte[] p3 = verticesProp.getElement(j++);
|
|
||||||
short x3 = LittleEndian.getShort(p3, 0);
|
|
||||||
short y3 = LittleEndian.getShort(p3, 2);
|
|
||||||
path.curveTo(
|
|
||||||
Units.masterToPoints(x1), Units.masterToPoints(y1),
|
|
||||||
Units.masterToPoints(x2), Units.masterToPoints(y2),
|
|
||||||
Units.masterToPoints(x3), Units.masterToPoints(y3));
|
|
||||||
|
|
||||||
} else if (Arrays.equals(elem, SEGMENTINFO_LINETO)){
|
byte segPushBack[] = null;
|
||||||
i++;
|
while (vertIter.hasNext() && segIter.hasNext()) {
|
||||||
byte[] pnext = segmentsProp.getElement(i);
|
byte[] segElem = (segPushBack != null) ? segPushBack : segIter.next();
|
||||||
if(Arrays.equals(pnext, SEGMENTINFO_ESCAPE)){
|
segPushBack = null;
|
||||||
if(j + 1 < numPoints){
|
PathInfo pi = getPathInfo(segElem);
|
||||||
byte[] p = verticesProp.getElement(j++);
|
switch (pi) {
|
||||||
short x = LittleEndian.getShort(p, 0);
|
case escape: {
|
||||||
short y = LittleEndian.getShort(p, 2);
|
handleEscapeInfo(path, segElem, vertIter);
|
||||||
path.lineTo(Units.masterToPoints(x), Units.masterToPoints(y));
|
break;
|
||||||
}
|
}
|
||||||
} else if (Arrays.equals(pnext, SEGMENTINFO_CLOSE)){
|
case moveTo: {
|
||||||
|
byte[] p = vertIter.next();
|
||||||
|
double x = Units.masterToPoints(LittleEndian.getShort(p, 0));
|
||||||
|
double y = Units.masterToPoints(LittleEndian.getShort(p, 2));
|
||||||
|
path.moveTo(x,y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case curveTo: {
|
||||||
|
byte[] p1 = vertIter.next();
|
||||||
|
double x1 = Units.masterToPoints(LittleEndian.getShort(p1, 0));
|
||||||
|
double y1 = Units.masterToPoints(LittleEndian.getShort(p1, 2));
|
||||||
|
byte[] p2 = vertIter.next();
|
||||||
|
double x2 = Units.masterToPoints(LittleEndian.getShort(p2, 0));
|
||||||
|
double y2 = Units.masterToPoints(LittleEndian.getShort(p2, 2));
|
||||||
|
byte[] p3 = vertIter.next();
|
||||||
|
double x3 = Units.masterToPoints(LittleEndian.getShort(p3, 0));
|
||||||
|
double y3 = Units.masterToPoints(LittleEndian.getShort(p3, 2));
|
||||||
|
path.curveTo(x1,y1,x2,y2,x3,y3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case lineTo:
|
||||||
|
if (vertIter.hasNext()) {
|
||||||
|
byte[] p = vertIter.next();
|
||||||
|
double x = Units.masterToPoints(LittleEndian.getShort(p, 0));
|
||||||
|
double y = Units.masterToPoints(LittleEndian.getShort(p, 2));
|
||||||
|
path.lineTo(x,y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case close:
|
||||||
path.closePath();
|
path.closePath();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EscherSimpleProperty shapePath = getShapeProp(opt, EscherProperties.GEOMETRY__SHAPEPATH);
|
||||||
|
ShapePath sp = ShapePath.valueOf(shapePath == null ? 1 : shapePath.getPropertyValue());
|
||||||
|
if (sp == ShapePath.LINES_CLOSED || sp == ShapePath.CURVES_CLOSED) {
|
||||||
|
path.closePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle2D anchor = getAnchor();
|
Rectangle2D anchor = getAnchor();
|
||||||
|
@ -251,6 +343,81 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
|
||||||
anchor.getWidth()/bounds.getWidth(),
|
anchor.getWidth()/bounds.getWidth(),
|
||||||
anchor.getHeight()/bounds.getHeight()
|
anchor.getHeight()/bounds.getHeight()
|
||||||
);
|
);
|
||||||
return new GeneralPath(at.createTransformedShape(path));
|
return new Path2D.Double(at.createTransformedShape(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends EscherProperty> T getShapeProp(AbstractEscherOptRecord opt, int propId) {
|
||||||
|
T prop = getEscherProperty(opt, (short)(propId + 0x4000));
|
||||||
|
if (prop == null) {
|
||||||
|
prop = getEscherProperty(opt, propId);
|
||||||
|
}
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEscapeInfo(Path2D path, byte segElem[], Iterator<byte[]> vertIter) {
|
||||||
|
EscapeInfo ei = getEscapeInfo(segElem);
|
||||||
|
switch (ei) {
|
||||||
|
case EXTENSION:
|
||||||
|
break;
|
||||||
|
case ANGLE_ELLIPSE_TO:
|
||||||
|
break;
|
||||||
|
case ANGLE_ELLIPSE:
|
||||||
|
break;
|
||||||
|
case ARC_TO:
|
||||||
|
break;
|
||||||
|
case ARC:
|
||||||
|
break;
|
||||||
|
case CLOCKWISE_ARC_TO:
|
||||||
|
break;
|
||||||
|
case CLOCKWISE_ARC:
|
||||||
|
break;
|
||||||
|
case ELLIPTICAL_QUADRANT_X:
|
||||||
|
break;
|
||||||
|
case ELLIPTICAL_QUADRANT_Y:
|
||||||
|
break;
|
||||||
|
case QUADRATIC_BEZIER:
|
||||||
|
break;
|
||||||
|
case NO_FILL:
|
||||||
|
break;
|
||||||
|
case NO_LINE:
|
||||||
|
break;
|
||||||
|
case AUTO_LINE:
|
||||||
|
break;
|
||||||
|
case AUTO_CURVE:
|
||||||
|
break;
|
||||||
|
case CORNER_LINE:
|
||||||
|
break;
|
||||||
|
case CORNER_CURVE:
|
||||||
|
break;
|
||||||
|
case SMOOTH_LINE:
|
||||||
|
break;
|
||||||
|
case SMOOTH_CURVE:
|
||||||
|
break;
|
||||||
|
case SYMMETRIC_LINE:
|
||||||
|
break;
|
||||||
|
case SYMMETRIC_CURVE:
|
||||||
|
break;
|
||||||
|
case FREEFORM:
|
||||||
|
break;
|
||||||
|
case FILL_COLOR:
|
||||||
|
break;
|
||||||
|
case LINE_COLOR:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static PathInfo getPathInfo(byte elem[]) {
|
||||||
|
int elemUS = LittleEndian.getUShort(elem, 0);
|
||||||
|
int pathInfo = PATH_INFO.getValue(elemUS);
|
||||||
|
return PathInfo.valueOf(pathInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EscapeInfo getEscapeInfo(byte elem[]) {
|
||||||
|
int elemUS = LittleEndian.getUShort(elem, 0);
|
||||||
|
int escInfo = ESCAPE_INFO.getValue(elemUS);
|
||||||
|
return EscapeInfo.valueOf(escInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -363,6 +363,9 @@ public final class HSLFHyperlink implements Hyperlink<HSLFShape,HSLFTextParagrap
|
||||||
|
|
||||||
InteractiveInfo hldr = (InteractiveInfo)r;
|
InteractiveInfo hldr = (InteractiveInfo)r;
|
||||||
InteractiveInfoAtom info = hldr.getInteractiveInfoAtom();
|
InteractiveInfoAtom info = hldr.getInteractiveInfoAtom();
|
||||||
|
if (info == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
int id = info.getHyperlinkID();
|
int id = info.getHyperlinkID();
|
||||||
ExHyperlink exHyper = exobj.get(id);
|
ExHyperlink exHyper = exobj.get(id);
|
||||||
if (exHyper == null) {
|
if (exHyper == null) {
|
||||||
|
|
|
@ -210,6 +210,13 @@ public class HSLFPictureShape extends HSLFSimpleShape implements PictureShape<HS
|
||||||
: new Insets((int)(top*100000), (int)(left*100000), (int)(bottom*100000), (int)(right*100000));
|
: new Insets((int)(top*100000), (int)(left*100000), (int)(bottom*100000), (int)(right*100000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ShapeType getShapeType() {
|
||||||
|
// this is kind of a hack, as picture/ole shapes can have a shape type of "frame"
|
||||||
|
// but rendering is handled like a rectangle
|
||||||
|
return ShapeType.RECT;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the fractional property or 0 if not defined
|
* @return the fractional property or 0 if not defined
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -346,7 +346,13 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
|
||||||
int val = (p == null) ? defaultColor : p.getPropertyValue();
|
int val = (p == null) ? defaultColor : p.getPropertyValue();
|
||||||
|
|
||||||
EscherColorRef ecr = new EscherColorRef(val);
|
EscherColorRef ecr = new EscherColorRef(val);
|
||||||
|
Color col = getColor(ecr);
|
||||||
|
|
||||||
|
double alpha = getAlpha(opacityProperty);
|
||||||
|
return new Color(col.getRed(), col.getGreen(), col.getBlue(), (int)(alpha*255.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getColor(EscherColorRef ecr) {
|
||||||
boolean fPaletteIndex = ecr.hasPaletteIndexFlag();
|
boolean fPaletteIndex = ecr.hasPaletteIndexFlag();
|
||||||
boolean fPaletteRGB = ecr.hasPaletteRGBFlag();
|
boolean fPaletteRGB = ecr.hasPaletteRGBFlag();
|
||||||
boolean fSystemRGB = ecr.hasSystemRGBFlag();
|
boolean fSystemRGB = ecr.hasSystemRGBFlag();
|
||||||
|
@ -374,8 +380,7 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
|
||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
double alpha = getAlpha(opacityProperty);
|
return new Color(rgb[0], rgb[1], rgb[2]);
|
||||||
return new Color(rgb[0], rgb[1], rgb[2], (int)(alpha*255.0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double getAlpha(short opacityProperty) {
|
double getAlpha(short opacityProperty) {
|
||||||
|
|
|
@ -111,8 +111,15 @@ public final class HSLFShapeFactory {
|
||||||
shape = createNonPrimitive(spContainer, parent);
|
shape = createNonPrimitive(spContainer, parent);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (parent instanceof HSLFTable) {
|
||||||
EscherTextboxRecord etr = spContainer.getChildById(EscherTextboxRecord.RECORD_ID);
|
EscherTextboxRecord etr = spContainer.getChildById(EscherTextboxRecord.RECORD_ID);
|
||||||
if (parent instanceof HSLFTable && etr != null) {
|
if (etr == null) {
|
||||||
|
logger.log(POILogger.WARN, "invalid ppt - add EscherTextboxRecord to cell");
|
||||||
|
etr = new EscherTextboxRecord();
|
||||||
|
etr.setRecordId(EscherTextboxRecord.RECORD_ID);
|
||||||
|
etr.setOptions((short)15);
|
||||||
|
spContainer.addChildRecord(etr);
|
||||||
|
}
|
||||||
shape = new HSLFTableCell(spContainer, (HSLFTable)parent);
|
shape = new HSLFTableCell(spContainer, (HSLFTable)parent);
|
||||||
} else {
|
} else {
|
||||||
shape = new HSLFAutoShape(spContainer, parent);
|
shape = new HSLFAutoShape(spContainer, parent);
|
||||||
|
|
|
@ -275,7 +275,8 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
|
||||||
|
|
||||||
public Guide getAdjustValue(String name) {
|
public Guide getAdjustValue(String name) {
|
||||||
if (name == null || !name.matches("adj([1-9]|10)?")) {
|
if (name == null || !name.matches("adj([1-9]|10)?")) {
|
||||||
throw new IllegalArgumentException("Adjust value '"+name+"' not supported.");
|
logger.log(POILogger.INFO, "Adjust value '"+name+"' not supported. Using default value.");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
name = name.replace("adj", "");
|
name = name.replace("adj", "");
|
||||||
|
@ -296,7 +297,13 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
|
||||||
default: throw new RuntimeException();
|
default: throw new RuntimeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: the adjust values need to be corrected somehow depending on the shape width/height
|
||||||
|
// see https://social.msdn.microsoft.com/Forums/en-US/3f69ebb3-62a0-4fdd-b367-64790dfb2491/presetshapedefinitionsxml-does-not-specify-width-and-height-form-some-autoshapes?forum=os_binaryfile
|
||||||
|
|
||||||
|
// the adjust value can be format dependent, e.g. hexagon has different values,
|
||||||
|
// other shape types have the same adjust values in OOXML and native
|
||||||
int adjval = getEscherProperty(escherProp, -1);
|
int adjval = getEscherProperty(escherProp, -1);
|
||||||
|
|
||||||
return (adjval == -1) ? null : new Guide(name, "val "+adjval);
|
return (adjval == -1) ? null : new Guide(name, "val "+adjval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -717,9 +717,25 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
|
||||||
String propNames[] = propName.split(",");
|
String propNames[] = propName.split(",");
|
||||||
for (String pn : propNames) {
|
for (String pn : propNames) {
|
||||||
TextProp prop = props.findByName(pn);
|
TextProp prop = props.findByName(pn);
|
||||||
if (prop != null) return prop;
|
if (prop == null) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Font properties (maybe other too???) can have an index of -1
|
||||||
|
// so we check the master for this font index then
|
||||||
|
if (pn.contains("font") && prop.getValue() == -1) {
|
||||||
|
return getMasterPropVal(props, pn, paragraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getMasterPropVal(props, propName, paragraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextProp getMasterPropVal(TextPropCollection props, String propName, HSLFTextParagraph paragraph) {
|
||||||
|
String propNames[] = propName.split(",");
|
||||||
|
|
||||||
BitMaskTextProp maskProp = (BitMaskTextProp) props.findByName(ParagraphFlagsTextProp.NAME);
|
BitMaskTextProp maskProp = (BitMaskTextProp) props.findByName(ParagraphFlagsTextProp.NAME);
|
||||||
boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0);
|
boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0);
|
||||||
if (hardAttribute) return null;
|
if (hardAttribute) return null;
|
||||||
|
|
|
@ -115,6 +115,9 @@ public class HwmfDrawProperties {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setViewportOrg(double x, double y) {
|
public void setViewportOrg(double x, double y) {
|
||||||
|
if (viewport == null) {
|
||||||
|
viewport = (Rectangle2D)window.clone();
|
||||||
|
}
|
||||||
double w = viewport.getWidth();
|
double w = viewport.getWidth();
|
||||||
double h = viewport.getHeight();
|
double h = viewport.getHeight();
|
||||||
viewport.setRect(x, y, w, h);
|
viewport.setRect(x, y, w, h);
|
||||||
|
|
|
@ -27,7 +27,6 @@ import java.awt.Shape;
|
||||||
import java.awt.TexturePaint;
|
import java.awt.TexturePaint;
|
||||||
import java.awt.font.TextAttribute;
|
import java.awt.font.TextAttribute;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.GeneralPath;
|
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.text.AttributedString;
|
import java.text.AttributedString;
|
||||||
|
@ -35,6 +34,7 @@ import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
import org.apache.poi.hwmf.record.HwmfBrushStyle;
|
import org.apache.poi.hwmf.record.HwmfBrushStyle;
|
||||||
|
@ -45,6 +45,9 @@ import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
|
||||||
import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
|
import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
|
||||||
import org.apache.poi.hwmf.record.HwmfPenStyle;
|
import org.apache.poi.hwmf.record.HwmfPenStyle;
|
||||||
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
|
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
|
||||||
|
import org.apache.poi.sl.draw.DrawFactory;
|
||||||
|
import org.apache.poi.sl.draw.DrawFontManager;
|
||||||
|
import org.apache.poi.sl.draw.Drawable;
|
||||||
|
|
||||||
public class HwmfGraphics {
|
public class HwmfGraphics {
|
||||||
private final Graphics2D graphicsCtx;
|
private final Graphics2D graphicsCtx;
|
||||||
|
@ -65,6 +68,7 @@ public class HwmfGraphics {
|
||||||
this.graphicsCtx = graphicsCtx;
|
this.graphicsCtx = graphicsCtx;
|
||||||
this.bbox = (Rectangle2D)bbox.clone();
|
this.bbox = (Rectangle2D)bbox.clone();
|
||||||
this.initialAT = graphicsCtx.getTransform();
|
this.initialAT = graphicsCtx.getTransform();
|
||||||
|
DrawFactory.getInstance(graphicsCtx).fixFonts(graphicsCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HwmfDrawProperties getProperties() {
|
public HwmfDrawProperties getProperties() {
|
||||||
|
@ -96,8 +100,8 @@ public class HwmfGraphics {
|
||||||
|
|
||||||
public void fill(Shape shape) {
|
public void fill(Shape shape) {
|
||||||
if (prop.getBrushStyle() != HwmfBrushStyle.BS_NULL) {
|
if (prop.getBrushStyle() != HwmfBrushStyle.BS_NULL) {
|
||||||
GeneralPath gp = new GeneralPath(shape);
|
// GeneralPath gp = new GeneralPath(shape);
|
||||||
gp.setWindingRule(prop.getPolyfillMode().awtFlag);
|
// gp.setWindingRule(prop.getPolyfillMode().awtFlag);
|
||||||
graphicsCtx.setPaint(getFill());
|
graphicsCtx.setPaint(getFill());
|
||||||
graphicsCtx.fill(shape);
|
graphicsCtx.fill(shape);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +115,12 @@ public class HwmfGraphics {
|
||||||
if (view == null) {
|
if (view == null) {
|
||||||
view = win;
|
view = win;
|
||||||
}
|
}
|
||||||
float width = (float)(prop.getPenWidth() * view.getWidth() / win.getWidth());
|
|
||||||
|
// TODO: fix line width calculation
|
||||||
|
float width = (float)prop.getPenWidth();
|
||||||
|
if (width == 0) {
|
||||||
|
width = 1;
|
||||||
|
}
|
||||||
HwmfPenStyle ps = prop.getPenStyle();
|
HwmfPenStyle ps = prop.getPenStyle();
|
||||||
int cap = ps.getLineCap().awtFlag;
|
int cap = ps.getLineCap().awtFlag;
|
||||||
int join = ps.getLineJoin().awtFlag;
|
int join = ps.getLineJoin().awtFlag;
|
||||||
|
@ -261,6 +270,10 @@ public class HwmfGraphics {
|
||||||
}
|
}
|
||||||
stackIndex = curIdx + index;
|
stackIndex = curIdx + index;
|
||||||
}
|
}
|
||||||
|
if (stackIndex == -1) {
|
||||||
|
// roll to last when curIdx == 0
|
||||||
|
stackIndex = propStack.size()-1;
|
||||||
|
}
|
||||||
prop = propStack.get(stackIndex);
|
prop = propStack.get(stackIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,14 +319,72 @@ public class HwmfGraphics {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawString(String text, Rectangle2D bounds) {
|
public void drawString(String text, Rectangle2D bounds) {
|
||||||
|
drawString(text, bounds, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawString(String text, Rectangle2D bounds, int dx[]) {
|
||||||
HwmfFont font = prop.getFont();
|
HwmfFont font = prop.getFont();
|
||||||
if (font == null) {
|
if (font == null || text == null || text.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double fontH = getFontHeight(font);
|
||||||
|
// TODO: another approx. ...
|
||||||
|
double fontW = fontH/1.8;
|
||||||
|
|
||||||
|
int len = text.length();
|
||||||
AttributedString as = new AttributedString(text);
|
AttributedString as = new AttributedString(text);
|
||||||
as.addAttribute(TextAttribute.FAMILY, font.getFacename());
|
if (dx == null || dx.length == 0) {
|
||||||
// TODO: fix font height calculation
|
addAttributes(as, font, 0, len);
|
||||||
as.addAttribute(TextAttribute.SIZE, Math.abs(font.getHeight()));
|
} else {
|
||||||
|
for (int i=0; i<len; i++) {
|
||||||
|
addAttributes(as, font, i, i+1);
|
||||||
|
// Tracking works as a prefix/advance space on characters whereas
|
||||||
|
// dx[...] is the complete width of the current char
|
||||||
|
// therefore we need to add the additional/suffix width to the next char
|
||||||
|
if (i<len-1) {
|
||||||
|
as.addAttribute(TextAttribute.TRACKING, (dx[i]-fontW)/fontH, i+1, i+2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double angle = Math.toRadians(-font.getEscapement()/10.);
|
||||||
|
|
||||||
|
|
||||||
|
final AffineTransform at = graphicsCtx.getTransform();
|
||||||
|
try {
|
||||||
|
graphicsCtx.translate(bounds.getX(), bounds.getY()+fontH);
|
||||||
|
graphicsCtx.rotate(angle);
|
||||||
|
if (prop.getBkMode() == HwmfBkMode.OPAQUE) {
|
||||||
|
// TODO: validate bounds
|
||||||
|
graphicsCtx.setBackground(prop.getBackgroundColor().getColor());
|
||||||
|
graphicsCtx.fill(new Rectangle2D.Double(0, 0, bounds.getWidth(), bounds.getHeight()));
|
||||||
|
}
|
||||||
|
graphicsCtx.setColor(prop.getTextColor().getColor());
|
||||||
|
graphicsCtx.drawString(as.getIterator(), 0, 0); // (float)bounds.getX(), (float)bounds.getY());
|
||||||
|
} finally {
|
||||||
|
graphicsCtx.setTransform(at);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAttributes(AttributedString as, HwmfFont font, int start, int end) {
|
||||||
|
DrawFontManager fontHandler = (DrawFontManager)graphicsCtx.getRenderingHint(Drawable.FONT_HANDLER);
|
||||||
|
String fontFamily = null;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String,String> fontMap = (Map<String,String>)graphicsCtx.getRenderingHint(Drawable.FONT_MAP);
|
||||||
|
if (fontMap != null && fontMap.containsKey(font.getFacename())) {
|
||||||
|
fontFamily = fontMap.get(font.getFacename());
|
||||||
|
}
|
||||||
|
if (fontHandler != null) {
|
||||||
|
fontFamily = fontHandler.getRendererableFont(font.getFacename(), font.getPitchAndFamily());
|
||||||
|
}
|
||||||
|
if (fontFamily == null) {
|
||||||
|
fontFamily = font.getFacename();
|
||||||
|
}
|
||||||
|
|
||||||
|
as.addAttribute(TextAttribute.FAMILY, fontFamily);
|
||||||
|
as.addAttribute(TextAttribute.SIZE, getFontHeight(font));
|
||||||
as.addAttribute(TextAttribute.STRIKETHROUGH, font.isStrikeOut());
|
as.addAttribute(TextAttribute.STRIKETHROUGH, font.isStrikeOut());
|
||||||
if (font.isUnderline()) {
|
if (font.isUnderline()) {
|
||||||
as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
|
as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
|
||||||
|
@ -322,23 +393,20 @@ public class HwmfGraphics {
|
||||||
as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
|
as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
|
||||||
}
|
}
|
||||||
as.addAttribute(TextAttribute.WEIGHT, font.getWeight());
|
as.addAttribute(TextAttribute.WEIGHT, font.getWeight());
|
||||||
|
}
|
||||||
|
|
||||||
double angle = Math.toRadians(-font.getEscapement()/10.);
|
private double getFontHeight(HwmfFont font) {
|
||||||
|
// see HwmfFont#height for details
|
||||||
|
double fontHeight = font.getHeight();
|
||||||
final AffineTransform at = graphicsCtx.getTransform();
|
if (fontHeight == 0) {
|
||||||
try {
|
return 12;
|
||||||
graphicsCtx.translate(bounds.getX(), bounds.getY());
|
} else if (fontHeight < 0) {
|
||||||
graphicsCtx.rotate(angle);
|
return -fontHeight;
|
||||||
if (prop.getBkMode() == HwmfBkMode.OPAQUE) {
|
} else {
|
||||||
// TODO: validate bounds
|
// TODO: fix font height calculation
|
||||||
graphicsCtx.setBackground(prop.getBackgroundColor().getColor());
|
// the height is given as font size + ascent + descent
|
||||||
graphicsCtx.fill(bounds);
|
// as an approximation we reduce the height by a static factor
|
||||||
}
|
return fontHeight*3/4;
|
||||||
graphicsCtx.setColor(prop.getTextColor().getColor());
|
|
||||||
graphicsCtx.drawString(as.getIterator(), 0, 0); // (float)bounds.getX(), (float)bounds.getY());
|
|
||||||
} finally {
|
|
||||||
graphicsCtx.setTransform(at);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
/* ====================================================================
|
||||||
|
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.record;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The BinaryRasterOperation Enumeration section lists the binary raster-operation codes.
|
||||||
|
* Rasteroperation codes define how metafile processing combines the bits from the selected
|
||||||
|
* pen with the bits in the destination bitmap.
|
||||||
|
*
|
||||||
|
* Each raster-operation code represents a Boolean operation in which the values of the pixels in the
|
||||||
|
* selected pen and the destination bitmap are combined. Following are the two operands used in these
|
||||||
|
* operations.
|
||||||
|
*
|
||||||
|
* <table>
|
||||||
|
* <tr><th>Operand</th><th>Meaning</th></tr>
|
||||||
|
* <tr><td>P</td><td>Selected pen</td></tr>
|
||||||
|
* <tr><td>D</td><td>Destination bitmap</td></tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* Following are the Boolean operators used in these operations.
|
||||||
|
* <table>
|
||||||
|
* <tr><th>Operand</th><th>Meaning</th></tr>
|
||||||
|
* <tr><td>a</td><td>Bitwise AND</td></tr>
|
||||||
|
* <tr><td>n</td><td>Bitwise NOT (inverse)</td></tr>
|
||||||
|
* <tr><td>o</td><td>Bitwise OR</td></tr>
|
||||||
|
* <tr><td>x</td><td>Bitwise exclusive OR (XOR)</td></tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* All Boolean operations are presented in reverse Polish notation. For example, the following
|
||||||
|
* operation replaces the values of the pixels in the destination bitmap with a combination of the pixel
|
||||||
|
* values of the pen and the selected brush: DPo.
|
||||||
|
*
|
||||||
|
* Each raster-operation code is a 32-bit integer whose high-order word is a Boolean operation index and
|
||||||
|
* whose low-order word is the operation code. The 16-bit operation index is a zero-extended, 8-bit
|
||||||
|
* value that represents all possible outcomes resulting from the Boolean operation on two parameters
|
||||||
|
* (in this case, the pen and destination values). For example, the operation indexes for the DPo and
|
||||||
|
* DPan operations are shown in the following list.
|
||||||
|
*
|
||||||
|
* <table>
|
||||||
|
* <tr><th>P</th><th>D</th><th>DPo</th><th>DPan</th></tr>
|
||||||
|
* <tr><td>0</td><td>0</td><td>0</td><td>1</td></tr>
|
||||||
|
* <tr><td>0</td><td>1</td><td>1</td><td>1</td></tr>
|
||||||
|
* <tr><td>1</td><td>0</td><td>1</td><td>1</td></tr>
|
||||||
|
* <tr><td>1</td><td>1</td><td>1</td><td>0</td></tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum HwmfBinaryRasterOp {
|
||||||
|
/** 0, Pixel is always 0 */
|
||||||
|
R2_BLACK(0x0001),
|
||||||
|
/** DPon, Pixel is the inverse of the R2_MERGEPEN color. */
|
||||||
|
R2_NOTMERGEPEN(0x0002),
|
||||||
|
/** DPna, Pixel is a combination of the screen color and the inverse of the pen color. */
|
||||||
|
R2_MASKNOTPEN(0x0003),
|
||||||
|
/** Pn, Pixel is the inverse of the pen color. */
|
||||||
|
R2_NOTCOPYPEN(0x0004),
|
||||||
|
/** PDna, Pixel is a combination of the colors common to both the pen and the inverse of the screen. */
|
||||||
|
R2_MASKPENNOT(0x0005),
|
||||||
|
/** Dn, Pixel is the inverse of the screen color. */
|
||||||
|
R2_NOT(0x0006),
|
||||||
|
/** DPx, Pixel is a combination of the colors in the pen or in the screen, but not in both. */
|
||||||
|
R2_XORPEN(0x0007),
|
||||||
|
/** DPan, Pixel is the inverse of the R2_MASKPEN color. */
|
||||||
|
R2_NOTMASKPEN(0x0008),
|
||||||
|
/** DPa, Pixel is a combination of the colors common to both the pen and the screen. */
|
||||||
|
R2_MASKPEN(0x0009),
|
||||||
|
/** DPxn, Pixel is the inverse of the R2_XORPEN color. */
|
||||||
|
R2_NOTXORPEN(0x000A),
|
||||||
|
/** D, Pixel remains unchanged. */
|
||||||
|
R2_NOP(0x000B),
|
||||||
|
/** DPno, Pixel is a combination of the colors common to both the screen and the inverse of the pen. */
|
||||||
|
R2_MERGENOTPEN(0x000C),
|
||||||
|
/** P, Pixel is the pen color. */
|
||||||
|
R2_COPYPEN(0x000D),
|
||||||
|
/** PDno, Pixel is a combination of the pen color and the inverse of the screen color.*/
|
||||||
|
R2_MERGEPENNOT(0x000E),
|
||||||
|
/** DPo, Pixel is a combination of the pen color and the screen color. */
|
||||||
|
R2_MERGEPEN(0x000F),
|
||||||
|
/** 1, Pixel is always 1 */
|
||||||
|
R2_WHITE(0x0010);
|
||||||
|
|
||||||
|
int opIndex;
|
||||||
|
|
||||||
|
HwmfBinaryRasterOp(int opIndex) {
|
||||||
|
this.opIndex=opIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HwmfBinaryRasterOp valueOf(int opIndex) {
|
||||||
|
for (HwmfBinaryRasterOp bb : HwmfBinaryRasterOp.values()) {
|
||||||
|
if (bb.opIndex == opIndex) {
|
||||||
|
return bb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,13 +18,11 @@
|
||||||
package org.apache.poi.hwmf.record;
|
package org.apache.poi.hwmf.record;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
@ -32,6 +30,8 @@ import org.apache.poi.hssf.record.RecordFormatException;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
import org.apache.poi.util.LittleEndianInputStream;
|
import org.apache.poi.util.LittleEndianInputStream;
|
||||||
|
import org.apache.poi.util.POILogFactory;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The DeviceIndependentBitmap Object defines an image in device-independent bitmap (DIB) format.
|
* The DeviceIndependentBitmap Object defines an image in device-independent bitmap (DIB) format.
|
||||||
|
@ -188,6 +188,7 @@ public class HwmfBitmapDib {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static POILogger logger = POILogFactory.getLogger(HwmfBitmapDib.class);
|
||||||
private static final int BMP_HEADER_SIZE = 14;
|
private static final int BMP_HEADER_SIZE = 14;
|
||||||
|
|
||||||
private int headerSize;
|
private int headerSize;
|
||||||
|
@ -204,8 +205,6 @@ public class HwmfBitmapDib {
|
||||||
private long headerColorUsed = -1;
|
private long headerColorUsed = -1;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private long headerColorImportant = -1;
|
private long headerColorImportant = -1;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private Color colorTable[];
|
private Color colorTable[];
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private int colorMaskR=0,colorMaskG=0,colorMaskB=0;
|
private int colorMaskR=0,colorMaskG=0,colorMaskB=0;
|
||||||
|
@ -360,22 +359,24 @@ public class HwmfBitmapDib {
|
||||||
|
|
||||||
protected int readRGBQuad(LittleEndianInputStream leis, int count) throws IOException {
|
protected int readRGBQuad(LittleEndianInputStream leis, int count) throws IOException {
|
||||||
int size = 0;
|
int size = 0;
|
||||||
List<Color> colorList = new ArrayList<Color>();
|
colorTable = new Color[count];
|
||||||
for (int i=0; i<count; i++) {
|
for (int i=0; i<count; i++) {
|
||||||
int blue = leis.readUByte();
|
int blue = leis.readUByte();
|
||||||
int green = leis.readUByte();
|
int green = leis.readUByte();
|
||||||
int red = leis.readUByte();
|
int red = leis.readUByte();
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
int reserved = leis.readUByte();
|
int reserved = leis.readUByte();
|
||||||
Color c = new Color(red, green, blue);
|
colorTable[i] = new Color(red, green, blue);
|
||||||
colorList.add(c);
|
|
||||||
size += 4 * LittleEndianConsts.BYTE_SIZE;
|
size += 4 * LittleEndianConsts.BYTE_SIZE;
|
||||||
}
|
}
|
||||||
colorTable = colorList.toArray(new Color[colorList.size()]);
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getBMPStream() {
|
public InputStream getBMPStream() {
|
||||||
|
return new ByteArrayInputStream(getBMPData());
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getBMPData() {
|
||||||
if (imageData == null) {
|
if (imageData == null) {
|
||||||
throw new RecordFormatException("bitmap not initialized ... need to call init() before");
|
throw new RecordFormatException("bitmap not initialized ... need to call init() before");
|
||||||
}
|
}
|
||||||
|
@ -398,14 +399,20 @@ public class HwmfBitmapDib {
|
||||||
// fill the "known" image data
|
// fill the "known" image data
|
||||||
System.arraycopy(imageData, 0, buf, BMP_HEADER_SIZE, imageData.length);
|
System.arraycopy(imageData, 0, buf, BMP_HEADER_SIZE, imageData.length);
|
||||||
|
|
||||||
return new ByteArrayInputStream(buf);
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferedImage getImage() {
|
public BufferedImage getImage() {
|
||||||
try {
|
try {
|
||||||
return ImageIO.read(getBMPStream());
|
return ImageIO.read(getBMPStream());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RecordFormatException("invalid bitmap data", e);
|
logger.log(POILogger.ERROR, "invalid bitmap data - returning black opaque image");
|
||||||
|
BufferedImage bi = new BufferedImage(headerWidth, headerHeight, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
Graphics2D g = bi.createGraphics();
|
||||||
|
g.setPaint(Color.black);
|
||||||
|
g.fillRect(0, 0, headerWidth, headerHeight);
|
||||||
|
g.dispose();
|
||||||
|
return bi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.apache.poi.hwmf.record;
|
package org.apache.poi.hwmf.record;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
import java.awt.geom.Arc2D;
|
import java.awt.geom.Arc2D;
|
||||||
import java.awt.geom.Area;
|
import java.awt.geom.Area;
|
||||||
|
@ -146,9 +147,10 @@ public class HwmfDraw {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
Path2D p = getShape();
|
Path2D shape = getShape();
|
||||||
p.closePath();
|
// shape.closePath();
|
||||||
p.setWindingRule(ctx.getProperties().getPolyfillMode().awtFlag);
|
Path2D p = (Path2D)shape.clone();
|
||||||
|
p.setWindingRule(getWindingRule(ctx));
|
||||||
ctx.fill(p);
|
ctx.fill(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,8 +172,9 @@ public class HwmfDraw {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
Path2D p = getShape();
|
Path2D shape = getShape();
|
||||||
p.setWindingRule(ctx.getProperties().getPolyfillMode().awtFlag);
|
Path2D p = (Path2D)shape.clone();
|
||||||
|
p.setWindingRule(getWindingRule(ctx));
|
||||||
ctx.draw(p);
|
ctx.draw(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,12 +323,13 @@ public class HwmfDraw {
|
||||||
|
|
||||||
for (int nPoints : pointsPerPolygon) {
|
for (int nPoints : pointsPerPolygon) {
|
||||||
/**
|
/**
|
||||||
* An array of 16-bit unsigned integers that define the coordinates of the polygons.
|
* An array of 16-bit signed integers that define the coordinates of the polygons.
|
||||||
|
* (Note: MS-WMF wrongly says unsigned integers ...)
|
||||||
*/
|
*/
|
||||||
Path2D poly = new Path2D.Double();
|
Path2D poly = new Path2D.Double();
|
||||||
for (int i=0; i<nPoints; i++) {
|
for (int i=0; i<nPoints; i++) {
|
||||||
int x = leis.readUShort();
|
int x = leis.readShort();
|
||||||
int y = leis.readUShort();
|
int y = leis.readShort();
|
||||||
size += 2*LittleEndianConsts.SHORT_SIZE;
|
size += 2*LittleEndianConsts.SHORT_SIZE;
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
poly.moveTo(x, y);
|
poly.moveTo(x, y);
|
||||||
|
@ -342,14 +346,23 @@ public class HwmfDraw {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
int windingRule = ctx.getProperties().getPolyfillMode().awtFlag;
|
if (polyList.isEmpty()) {
|
||||||
Area area = new Area();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int windingRule = getWindingRule(ctx);
|
||||||
|
Area area = null;
|
||||||
for (Path2D poly : polyList) {
|
for (Path2D poly : polyList) {
|
||||||
Path2D p = (Path2D)poly.clone();
|
Path2D p = (Path2D)poly.clone();
|
||||||
p.setWindingRule(windingRule);
|
p.setWindingRule(windingRule);
|
||||||
area.add(new Area(p));
|
Area newArea = new Area(p);
|
||||||
|
if (area == null) {
|
||||||
|
area = newArea;
|
||||||
|
} else {
|
||||||
|
area.exclusiveOr(newArea);
|
||||||
}
|
}
|
||||||
ctx.draw(area);
|
}
|
||||||
|
ctx.fill(area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,4 +692,8 @@ public class HwmfDraw {
|
||||||
ctx.applyObjectTableEntry(objectIndex);
|
ctx.applyObjectTableEntry(objectIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getWindingRule(HwmfGraphics ctx) {
|
||||||
|
return ctx.getProperties().getPolyfillMode().awtFlag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,155 @@ import org.apache.poi.util.HexDump;
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
import org.apache.poi.util.LittleEndianInputStream;
|
import org.apache.poi.util.LittleEndianInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MetafileEscapes specifies printer driver functionality that
|
||||||
|
* might not be directly accessible through WMF records
|
||||||
|
*/
|
||||||
public class HwmfEscape implements HwmfRecord {
|
public class HwmfEscape implements HwmfRecord {
|
||||||
|
|
||||||
|
public enum EscapeFunction {
|
||||||
|
/** Notifies the printer driver that the application has finished writing to a page. */
|
||||||
|
NEWFRAME(0x0001),
|
||||||
|
/** Stops processing the current document. */
|
||||||
|
ABORTDOC(0x0002),
|
||||||
|
/** Notifies the printer driver that the application has finished writing to a band. */
|
||||||
|
NEXTBAND(0x0003),
|
||||||
|
/** Sets color table values. */
|
||||||
|
SETCOLORTABLE(0x0004),
|
||||||
|
/** Gets color table values. */
|
||||||
|
GETCOLORTABLE(0x0005),
|
||||||
|
/** Causes all pending output to be flushed to the output device. */
|
||||||
|
FLUSHOUT(0x0006),
|
||||||
|
/** Indicates that the printer driver SHOULD print text only, and no graphics. */
|
||||||
|
DRAFTMODE(0x0007),
|
||||||
|
/** Queries a printer driver to determine whether a specific escape function is supported on the output device it drives. */
|
||||||
|
QUERYESCSUPPORT(0x0008),
|
||||||
|
/** Sets the application-defined function that allows a print job to be canceled during printing. */
|
||||||
|
SETABORTPROC(0x0009),
|
||||||
|
/** Notifies the printer driver that a new print job is starting. */
|
||||||
|
STARTDOC(0x000A),
|
||||||
|
/** Notifies the printer driver that the current print job is ending. */
|
||||||
|
ENDDOC(0x000B),
|
||||||
|
/** Retrieves the physical page size currently selected on an output device. */
|
||||||
|
GETPHYSPAGESIZE(0x000C),
|
||||||
|
/** Retrieves the offset from the upper-left corner of the physical page where the actual printing or drawing begins. */
|
||||||
|
GETPRINTINGOFFSET(0x000D),
|
||||||
|
/** Retrieves the scaling factors for the x-axis and the y-axis of a printer. */
|
||||||
|
GETSCALINGFACTOR(0x000E),
|
||||||
|
/** Used to embed an enhanced metafile format (EMF) metafile within a WMF metafile. */
|
||||||
|
META_ESCAPE_ENHANCED_METAFILE(0x000F),
|
||||||
|
/** Sets the width of a pen in pixels. */
|
||||||
|
SETPENWIDTH(0x0010),
|
||||||
|
/** Sets the number of copies. */
|
||||||
|
SETCOPYCOUNT(0x0011),
|
||||||
|
/** Sets the source, such as a particular paper tray or bin on a printer, for output forms. */
|
||||||
|
SETPAPERSOURCE(0x0012),
|
||||||
|
/** This record passes through arbitrary data. */
|
||||||
|
PASSTHROUGH(0x0013),
|
||||||
|
/** Gets information concerning graphics technology that is supported on a device. */
|
||||||
|
GETTECHNOLOGY(0x0014),
|
||||||
|
/** Specifies the line-drawing mode to use in output to a device. */
|
||||||
|
SETLINECAP(0x0015),
|
||||||
|
/** Specifies the line-joining mode to use in output to a device. */
|
||||||
|
SETLINEJOIN(0x0016),
|
||||||
|
/** Sets the limit for the length of miter joins to use in output to a device. */
|
||||||
|
SETMITERLIMIT(0x0017),
|
||||||
|
/** Retrieves or specifies settings concerning banding on a device, such as the number of bands. */
|
||||||
|
BANDINFO(0x0018),
|
||||||
|
/** Draws a rectangle with a defined pattern. */
|
||||||
|
DRAWPATTERNRECT(0x0019),
|
||||||
|
/** Retrieves the physical pen size currently defined on a device. */
|
||||||
|
GETVECTORPENSIZE(0x001A),
|
||||||
|
/** Retrieves the physical brush size currently defined on a device. */
|
||||||
|
GETVECTORBRUSHSIZE(0x001B),
|
||||||
|
/** Enables or disables double-sided (duplex) printing on a device. */
|
||||||
|
ENABLEDUPLEX(0x001C),
|
||||||
|
/** Retrieves or specifies the source of output forms on a device. */
|
||||||
|
GETSETPAPERBINS(0x001D),
|
||||||
|
/** Retrieves or specifies the paper orientation on a device. */
|
||||||
|
GETSETPRINTORIENT(0x001E),
|
||||||
|
/** Retrieves information concerning the sources of different forms on an output device. */
|
||||||
|
ENUMPAPERBINS(0x001F),
|
||||||
|
/** Specifies the scaling of device-independent bitmaps (DIBs). */
|
||||||
|
SETDIBSCALING(0x0020),
|
||||||
|
/** Indicates the start and end of an encapsulated PostScript (EPS) section. */
|
||||||
|
EPSPRINTING(0x0021),
|
||||||
|
/** Queries a printer driver for paper dimensions and other forms data. */
|
||||||
|
ENUMPAPERMETRICS(0x0022),
|
||||||
|
/** Retrieves or specifies paper dimensions and other forms data on an output device. */
|
||||||
|
GETSETPAPERMETRICS(0x0023),
|
||||||
|
/** Sends arbitrary PostScript data to an output device. */
|
||||||
|
POSTSCRIPT_DATA(0x0025),
|
||||||
|
/** Notifies an output device to ignore PostScript data. */
|
||||||
|
POSTSCRIPT_IGNORE(0x0026),
|
||||||
|
/** Gets the device units currently configured on an output device. */
|
||||||
|
GETDEVICEUNITS(0x002A),
|
||||||
|
/** Gets extended text metrics currently configured on an output device. */
|
||||||
|
GETEXTENDEDTEXTMETRICS(0x0100),
|
||||||
|
/** Gets the font kern table currently defined on an output device. */
|
||||||
|
GETPAIRKERNTABLE(0x0102),
|
||||||
|
/** Draws text using the currently selected font, background color, and text color. */
|
||||||
|
EXTTEXTOUT(0x0200),
|
||||||
|
/** Gets the font face name currently configured on a device. */
|
||||||
|
GETFACENAME(0x0201),
|
||||||
|
/** Sets the font face name on a device. */
|
||||||
|
DOWNLOADFACE(0x0202),
|
||||||
|
/** Queries a printer driver about the support for metafiles on an output device. */
|
||||||
|
METAFILE_DRIVER(0x0801),
|
||||||
|
/** Queries the printer driver about its support for DIBs on an output device. */
|
||||||
|
QUERYDIBSUPPORT(0x0C01),
|
||||||
|
/** Opens a path. */
|
||||||
|
BEGIN_PATH(0x1000),
|
||||||
|
/** Defines a clip region that is bounded by a path. The input MUST be a 16-bit quantity that defines the action to take. */
|
||||||
|
CLIP_TO_PATH(0x1001),
|
||||||
|
/** Ends a path. */
|
||||||
|
END_PATH(0x1002),
|
||||||
|
/** The same as STARTDOC specified with a NULL document and output filename, data in raw mode, and a type of zero. */
|
||||||
|
OPEN_CHANNEL(0x100E),
|
||||||
|
/** Instructs the printer driver to download sets of PostScript procedures. */
|
||||||
|
DOWNLOADHEADER(0x100F),
|
||||||
|
/** The same as ENDDOC. See OPEN_CHANNEL. */
|
||||||
|
CLOSE_CHANNEL(0x1010),
|
||||||
|
/** Sends arbitrary data directly to a printer driver, which is expected to process this data only when in PostScript mode. */
|
||||||
|
POSTSCRIPT_PASSTHROUGH(0x1013),
|
||||||
|
/** Sends arbitrary data directly to the printer driver. */
|
||||||
|
ENCAPSULATED_POSTSCRIPT(0x1014),
|
||||||
|
/** Sets the printer driver to either PostScript or GDI mode. */
|
||||||
|
POSTSCRIPT_IDENTIFY(0x1015),
|
||||||
|
/** Inserts a block of raw data into a PostScript stream. The input MUST be
|
||||||
|
a 32-bit quantity specifying the number of bytes to inject, a 16-bit quantity specifying the
|
||||||
|
injection point, and a 16-bit quantity specifying the page number, followed by the bytes to
|
||||||
|
inject. */
|
||||||
|
POSTSCRIPT_INJECTION(0x1016),
|
||||||
|
/** Checks whether the printer supports a JPEG image. */
|
||||||
|
CHECKJPEGFORMAT(0x1017),
|
||||||
|
/** Checks whether the printer supports a PNG image */
|
||||||
|
CHECKPNGFORMAT(0x1018),
|
||||||
|
/** Gets information on a specified feature setting for a PostScript printer driver. */
|
||||||
|
GET_PS_FEATURESETTING(0x1019),
|
||||||
|
/** Enables applications to write documents to a file or to a printer in XML Paper Specification (XPS) format. */
|
||||||
|
MXDC_ESCAPE(0x101A),
|
||||||
|
/** Enables applications to include private procedures and other arbitrary data in documents. */
|
||||||
|
SPCLPASSTHROUGH2(0x11D8);
|
||||||
|
|
||||||
|
int flag;
|
||||||
|
EscapeFunction(int flag) {
|
||||||
|
this.flag = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EscapeFunction valueOf(int flag) {
|
||||||
|
for (EscapeFunction hs : values()) {
|
||||||
|
if (hs.flag == flag) return hs;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 16-bit unsigned integer that defines the escape function. The
|
* A 16-bit unsigned integer that defines the escape function. The
|
||||||
* value MUST be from the MetafileEscapes enumeration.
|
* value MUST be from the MetafileEscapes enumeration.
|
||||||
*/
|
*/
|
||||||
private int escapeFunction;
|
private EscapeFunction escapeFunction;
|
||||||
/**
|
/**
|
||||||
* A 16-bit unsigned integer that specifies the size, in bytes, of the
|
* A 16-bit unsigned integer that specifies the size, in bytes, of the
|
||||||
* EscapeData field.
|
* EscapeData field.
|
||||||
|
@ -48,7 +190,7 @@ public class HwmfEscape implements HwmfRecord {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
||||||
escapeFunction = leis.readUShort();
|
escapeFunction = EscapeFunction.valueOf(leis.readUShort());
|
||||||
byteCount = leis.readUShort();
|
byteCount = leis.readUShort();
|
||||||
escapeData = new byte[byteCount];
|
escapeData = new byte[byteCount];
|
||||||
leis.read(escapeData);
|
leis.read(escapeData);
|
||||||
|
|
|
@ -20,13 +20,9 @@ package org.apache.poi.hwmf.record;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
||||||
import org.apache.poi.hwmf.record.HwmfWindowing.WmfCreateRegion;
|
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
import org.apache.poi.util.LittleEndianInputStream;
|
import org.apache.poi.util.LittleEndianInputStream;
|
||||||
|
|
||||||
|
@ -240,7 +236,7 @@ public class HwmfFill {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
||||||
polyfillMode = HwmfPolyfillMode.valueOf(leis.readUShort());
|
polyfillMode = HwmfPolyfillMode.valueOf(leis.readUShort() & 3);
|
||||||
return LittleEndianConsts.SHORT_SIZE;
|
return LittleEndianConsts.SHORT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,6 +398,15 @@ public class HwmfFill {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The META_STRETCHBLT record specifies the transfer of a block of pixels 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_STRETCHBLT, one which specifies a bitmap 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_STRETCHBLT are defined below. The subsections that follow specify
|
||||||
|
* the packet structures of the two forms of META_STRETCHBLT.
|
||||||
|
* The expansion or contraction is performed according to the stretching mode currently set in the
|
||||||
|
* playback device context, which MUST be a value from the StretchMode.
|
||||||
*/
|
*/
|
||||||
public static class WmfStretchBlt implements HwmfRecord {
|
public static class WmfStretchBlt implements HwmfRecord {
|
||||||
/**
|
/**
|
||||||
|
@ -507,7 +512,7 @@ public class HwmfFill {
|
||||||
* The source of the color data is a DIB, and the destination of the transfer is
|
* The source of the color data is a DIB, and the destination of the transfer is
|
||||||
* the current output region in the playback device context.
|
* the current output region in the playback device context.
|
||||||
*/
|
*/
|
||||||
public static class WmfStretchDib implements HwmfRecord, HwmfImageRecord {
|
public static class WmfStretchDib implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry {
|
||||||
/**
|
/**
|
||||||
* A 32-bit unsigned integer that defines how the source pixels, the current brush in
|
* 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
|
* the playback device context, and the destination pixels are to be combined to
|
||||||
|
@ -599,6 +604,11 @@ public class HwmfFill {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -706,7 +716,7 @@ public class HwmfFill {
|
||||||
* using deviceindependent color data.
|
* using deviceindependent color data.
|
||||||
* The source of the color data is a DIB
|
* The source of the color data is a DIB
|
||||||
*/
|
*/
|
||||||
public static class WmfSetDibToDev implements HwmfRecord, HwmfImageRecord {
|
public static class WmfSetDibToDev implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 16-bit unsigned integer that defines whether the Colors field of the
|
* A 16-bit unsigned integer that defines whether the Colors field of the
|
||||||
|
@ -783,6 +793,11 @@ public class HwmfFill {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -793,7 +808,7 @@ public class HwmfFill {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class WmfDibBitBlt implements HwmfRecord, HwmfImageRecord {
|
public static class WmfDibBitBlt implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 32-bit unsigned integer that defines how the source pixels, the current brush
|
* A 32-bit unsigned integer that defines how the source pixels, the current brush
|
||||||
|
@ -877,6 +892,11 @@ public class HwmfFill {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -886,7 +906,7 @@ public class HwmfFill {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class WmfDibStretchBlt implements HwmfRecord, HwmfImageRecord {
|
public static class WmfDibStretchBlt implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry {
|
||||||
/**
|
/**
|
||||||
* A 32-bit unsigned integer that defines how the source pixels, the current brush
|
* 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
|
* in the playback device context, and the destination pixels are to be combined to form the
|
||||||
|
@ -978,6 +998,11 @@ public class HwmfFill {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -448,6 +448,13 @@ public class HwmfFont {
|
||||||
*/
|
*/
|
||||||
WmfFontQuality quality;
|
WmfFontQuality quality;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A PitchAndFamily object that defines the pitch and the family of the font.
|
||||||
|
* Font families specify the look of fonts in a general way and are intended for
|
||||||
|
* specifying fonts when the exact typeface wanted is not available.
|
||||||
|
*/
|
||||||
|
int pitchAndFamily;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Font families specify the look of fonts in a general way and are
|
* Font families specify the look of fonts in a general way and are
|
||||||
* intended for specifying fonts when the exact typeface wanted is not available.
|
* intended for specifying fonts when the exact typeface wanted is not available.
|
||||||
|
@ -480,9 +487,7 @@ public class HwmfFont {
|
||||||
outPrecision = WmfOutPrecision.valueOf(leis.readUByte());
|
outPrecision = WmfOutPrecision.valueOf(leis.readUByte());
|
||||||
clipPrecision = WmfClipPrecision.valueOf(leis.readUByte());
|
clipPrecision = WmfClipPrecision.valueOf(leis.readUByte());
|
||||||
quality = WmfFontQuality.valueOf(leis.readUByte());
|
quality = WmfFontQuality.valueOf(leis.readUByte());
|
||||||
int pitchAndFamily = leis.readUByte();
|
pitchAndFamily = leis.readUByte();
|
||||||
family = WmfFontFamilyClass.valueOf(pitchAndFamily & 0xF);
|
|
||||||
pitch = WmfFontPitch.valueOf((pitchAndFamily >>> 6) & 3);
|
|
||||||
|
|
||||||
byte buf[] = new byte[32], b, readBytes = 0;
|
byte buf[] = new byte[32], b, readBytes = 0;
|
||||||
do {
|
do {
|
||||||
|
@ -546,12 +551,16 @@ public class HwmfFont {
|
||||||
return quality;
|
return quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPitchAndFamily() {
|
||||||
|
return pitchAndFamily;
|
||||||
|
}
|
||||||
|
|
||||||
public WmfFontFamilyClass getFamily() {
|
public WmfFontFamilyClass getFamily() {
|
||||||
return family;
|
return WmfFontFamilyClass.valueOf(pitchAndFamily & 0xF);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WmfFontPitch getPitch() {
|
public WmfFontPitch getPitch() {
|
||||||
return pitch;
|
return WmfFontPitch.valueOf((pitchAndFamily >>> 6) & 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFacename() {
|
public String getFacename() {
|
||||||
|
|
|
@ -109,6 +109,6 @@ public enum HwmfMapMode {
|
||||||
for (HwmfMapMode mm : values()) {
|
for (HwmfMapMode mm : values()) {
|
||||||
if (mm.flag == flag) return mm;
|
if (mm.flag == flag) return mm;
|
||||||
}
|
}
|
||||||
return null;
|
return MM_ISOTROPIC;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -265,25 +265,9 @@ public class HwmfMisc {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 16-bit unsigned integer that defines the foreground binary raster
|
* A 16-bit unsigned integer that defines the foreground binary raster
|
||||||
* operation mixing mode. This MUST be one of the values:
|
* operation mixing mode
|
||||||
* R2_BLACK = 0x0001,
|
|
||||||
* R2_NOTMERGEPEN = 0x0002,
|
|
||||||
* R2_MASKNOTPEN = 0x0003,
|
|
||||||
* R2_NOTCOPYPEN = 0x0004,
|
|
||||||
* R2_MASKPENNOT = 0x0005,
|
|
||||||
* R2_NOT = 0x0006,
|
|
||||||
* R2_XORPEN = 0x0007,
|
|
||||||
* R2_NOTMASKPEN = 0x0008,
|
|
||||||
* R2_MASKPEN = 0x0009,
|
|
||||||
* R2_NOTXORPEN = 0x000A,
|
|
||||||
* R2_NOP = 0x000B,
|
|
||||||
* R2_MERGENOTPEN = 0x000C,
|
|
||||||
* R2_COPYPEN = 0x000D,
|
|
||||||
* R2_MERGEPENNOT = 0x000E,
|
|
||||||
* R2_MERGEPEN = 0x000F,
|
|
||||||
* R2_WHITE = 0x0010
|
|
||||||
*/
|
*/
|
||||||
private int drawMode;
|
private HwmfBinaryRasterOp drawMode;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HwmfRecordType getRecordType() {
|
public HwmfRecordType getRecordType() {
|
||||||
|
@ -292,7 +276,7 @@ public class HwmfMisc {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
|
||||||
drawMode = leis.readUShort();
|
drawMode = HwmfBinaryRasterOp.valueOf(leis.readUShort());
|
||||||
return LittleEndianConsts.SHORT_SIZE;
|
return LittleEndianConsts.SHORT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ public class HwmfPalette {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static abstract class WmfPaletteParent implements HwmfRecord {
|
public static abstract class WmfPaletteParent implements HwmfRecord, HwmfObjectTableEntry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start (2 bytes): A 16-bit unsigned integer that defines the offset into the Palette Object when
|
* Start (2 bytes): A 16-bit unsigned integer that defines the offset into the Palette Object when
|
||||||
|
@ -121,6 +121,11 @@ public class HwmfPalette {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void draw(HwmfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
protected List<PaletteEntry> getPaletteCopy() {
|
protected List<PaletteEntry> getPaletteCopy() {
|
||||||
List<PaletteEntry> newPalette = new ArrayList<PaletteEntry>();
|
List<PaletteEntry> newPalette = new ArrayList<PaletteEntry>();
|
||||||
for (PaletteEntry et : palette) {
|
for (PaletteEntry et : palette) {
|
||||||
|
@ -143,11 +148,6 @@ public class HwmfPalette {
|
||||||
return HwmfRecordType.createPalette;
|
return HwmfRecordType.createPalette;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(HwmfGraphics ctx) {
|
|
||||||
ctx.addObjectTableEntry(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyObject(HwmfGraphics ctx) {
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
ctx.getProperties().setPalette(getPaletteCopy());
|
ctx.getProperties().setPalette(getPaletteCopy());
|
||||||
|
@ -165,7 +165,7 @@ public class HwmfPalette {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
HwmfDrawProperties props = ctx.getProperties();
|
HwmfDrawProperties props = ctx.getProperties();
|
||||||
List<PaletteEntry> palette = props.getPalette();
|
List<PaletteEntry> palette = props.getPalette();
|
||||||
if (palette == null) {
|
if (palette == null) {
|
||||||
|
@ -192,7 +192,7 @@ public class HwmfPalette {
|
||||||
* The META_RESIZEPALETTE record redefines the size of the logical palette that is defined in the
|
* The META_RESIZEPALETTE record redefines the size of the logical palette that is defined in the
|
||||||
* playback device context.
|
* playback device context.
|
||||||
*/
|
*/
|
||||||
public static class WmfResizePalette implements HwmfRecord {
|
public static class WmfResizePalette implements HwmfRecord, HwmfObjectTableEntry {
|
||||||
/**
|
/**
|
||||||
* A 16-bit unsigned integer that defines the number of entries in
|
* A 16-bit unsigned integer that defines the number of entries in
|
||||||
* the logical palette.
|
* the logical palette.
|
||||||
|
@ -212,6 +212,11 @@ public class HwmfPalette {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
HwmfDrawProperties props = ctx.getProperties();
|
HwmfDrawProperties props = ctx.getProperties();
|
||||||
List<PaletteEntry> palette = props.getPalette();
|
List<PaletteEntry> palette = props.getPalette();
|
||||||
if (palette == null) {
|
if (palette == null) {
|
||||||
|
@ -292,7 +297,7 @@ public class HwmfPalette {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
HwmfDrawProperties props = ctx.getProperties();
|
HwmfDrawProperties props = ctx.getProperties();
|
||||||
List<PaletteEntry> dest = props.getPalette();
|
List<PaletteEntry> dest = props.getPalette();
|
||||||
List<PaletteEntry> src = getPaletteCopy();
|
List<PaletteEntry> src = getPaletteCopy();
|
||||||
|
|
|
@ -17,6 +17,68 @@
|
||||||
|
|
||||||
package org.apache.poi.hwmf.record;
|
package org.apache.poi.hwmf.record;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each ternary raster operation code represents a Boolean operation in which the values of the pixels in
|
||||||
|
* the source, the selected brush, and the destination are combined. Following are the three operands
|
||||||
|
* used in these operations.
|
||||||
|
*
|
||||||
|
* <table>
|
||||||
|
* <tr><th>Operand</th><th>Meaning</th></tr>
|
||||||
|
* <tr><td>D</td><td>Destination bitmap</td></tr>
|
||||||
|
* <tr><td>P</td><td>Selected brush (also called pattern)</td></tr>
|
||||||
|
* <tr><td>S</td><td>Source bitmap</td></tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* Following are the Boolean operators used in these operations.
|
||||||
|
* <table>
|
||||||
|
* <tr><th>Operand</th><th>Meaning</th></tr>
|
||||||
|
* <tr><td>a</td><td>Bitwise AND</td></tr>
|
||||||
|
* <tr><td>n</td><td>Bitwise NOT (inverse)</td></tr>
|
||||||
|
* <tr><td>o</td><td>Bitwise OR</td></tr>
|
||||||
|
* <tr><td>x</td><td>Bitwise exclusive OR (XOR)</td></tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* All Boolean operations are presented in reverse Polish notation. For example, the following operation
|
||||||
|
* replaces the values of the pixels in the destination bitmap with a combination of the pixel values of the
|
||||||
|
* source and brush: PSo.
|
||||||
|
*
|
||||||
|
* The following operation combines the values of the pixels in the source and brush with the pixel values
|
||||||
|
* of the destination bitmap: DPSoo (there are alternative spellings of some functions, so although a
|
||||||
|
* particular spelling MAY NOT be listed in the enumeration, an equivalent form SHOULD be).
|
||||||
|
*
|
||||||
|
* Each raster operation code is a 32-bit integer whose high-order word is a Boolean operation index and
|
||||||
|
* whose low-order word is the operation code. The 16-bit operation index is a zero-extended, 8-bit
|
||||||
|
* value that represents the result of the Boolean operation on predefined brush, source, and destination
|
||||||
|
* values. For example, the operation indexes for the PSo and DPSoo operations are shown in the
|
||||||
|
* following list.
|
||||||
|
*
|
||||||
|
* <table>
|
||||||
|
* <tr><th>P</th><th>S</th><th>D</th><th>DPo</th><th>DPan</th></tr>
|
||||||
|
* <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr>
|
||||||
|
* <tr><td>0</td><td>0</td><td>1</td><td>0</td><td>1</td></tr>
|
||||||
|
* <tr><td>0</td><td>1</td><td>0</td><td>1</td><td>1</td></tr>
|
||||||
|
* <tr><td>0</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
|
||||||
|
* <tr><td>1</td><td>0</td><td>0</td><td>1</td><td>1</td></tr>
|
||||||
|
* <tr><td>1</td><td>0</td><td>1</td><td>1</td><td>1</td></tr>
|
||||||
|
* <tr><td>1</td><td>1</td><td>0</td><td>1</td><td>1</td></tr>
|
||||||
|
* <tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* The operation indexes are determined by reading the binary values in a column of the table from the
|
||||||
|
* bottom up. For example, in the PSo column, the binary value is 11111100, which is equivalent to 00FC
|
||||||
|
* (hexadecimal is implicit for these values), which is the operation index for PSo.
|
||||||
|
*
|
||||||
|
* Using this method, DPSoo can be seen to have the operation index 00FE. Operation indexes define the
|
||||||
|
* locations of corresponding raster operation codes in the preceding enumeration. The PSo operation is
|
||||||
|
* in line 252 (0x00FC) of the enumeration; DPSoo is in line 254 (0x00FE).
|
||||||
|
*
|
||||||
|
* The most commonly used raster operations have been given explicit enumeration names, which
|
||||||
|
* SHOULD be used; examples are PATCOPY and WHITENESS.
|
||||||
|
*
|
||||||
|
* When the source and destination bitmaps are monochrome, a bit value of 0 represents a black pixel
|
||||||
|
* and a bit value of 1 represents a white pixel. When the source and the destination bitmaps are color,
|
||||||
|
* those colors are represented with red green blue (RGB) values.
|
||||||
|
*/
|
||||||
public enum HwmfTernaryRasterOp {
|
public enum HwmfTernaryRasterOp {
|
||||||
BLACKNESS(0x0000,0x0042,"0"),
|
BLACKNESS(0x0000,0x0042,"0"),
|
||||||
DPSOON(0x0001,0x0289,"DPSoon"),
|
DPSOON(0x0001,0x0289,"DPSoon"),
|
||||||
|
|
|
@ -19,7 +19,6 @@ package org.apache.poi.hwmf.record;
|
||||||
|
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.AttributedString;
|
|
||||||
|
|
||||||
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
|
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
|
||||||
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
import org.apache.poi.hwmf.draw.HwmfGraphics;
|
||||||
|
@ -29,9 +28,12 @@ import org.apache.poi.util.BitFieldFactory;
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
import org.apache.poi.util.LittleEndianInputStream;
|
import org.apache.poi.util.LittleEndianInputStream;
|
||||||
import org.apache.poi.util.LocaleUtil;
|
import org.apache.poi.util.LocaleUtil;
|
||||||
|
import org.apache.poi.util.POILogFactory;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
import org.apache.poi.util.RecordFormatException;
|
import org.apache.poi.util.RecordFormatException;
|
||||||
|
|
||||||
public class HwmfText {
|
public class HwmfText {
|
||||||
|
private static final POILogger logger = POILogFactory.getLogger(HwmfText.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The META_SETTEXTCHAREXTRA record defines inter-character spacing for text justification in the
|
* The META_SETTEXTCHAREXTRA record defines inter-character spacing for text justification in the
|
||||||
|
@ -185,6 +187,48 @@ public class HwmfText {
|
||||||
*/
|
*/
|
||||||
public static class WmfExtTextOut implements HwmfRecord {
|
public static class WmfExtTextOut implements HwmfRecord {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the background color that is defined in the playback device context
|
||||||
|
* SHOULD be used to fill the rectangle.
|
||||||
|
*/
|
||||||
|
private static final BitField ETO_OPAQUE = BitFieldFactory.getInstance(0x0002);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the text SHOULD be clipped to the rectangle.
|
||||||
|
*/
|
||||||
|
private static final BitField ETO_CLIPPED = BitFieldFactory.getInstance(0x0004);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the string to be output SHOULD NOT require further processing
|
||||||
|
* with respect to the placement of the characters, and an array of character
|
||||||
|
* placement values SHOULD be provided. This character placement process is
|
||||||
|
* useful for fonts in which diacritical characters affect character spacing.
|
||||||
|
*/
|
||||||
|
private static final BitField ETO_GLYPH_INDEX = BitFieldFactory.getInstance(0x0010);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the text MUST be laid out in right-to-left reading order, instead of
|
||||||
|
* the default left-to-right order. This SHOULD be applied only when the font that is
|
||||||
|
* defined in the playback device context is either Hebrew or Arabic.
|
||||||
|
*/
|
||||||
|
private static final BitField ETO_RTLREADING = BitFieldFactory.getInstance(0x0080);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that to display numbers, digits appropriate to the locale SHOULD be used.
|
||||||
|
*/
|
||||||
|
private static final BitField ETO_NUMERICSLOCAL = BitFieldFactory.getInstance(0x0400);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that to display numbers, European digits SHOULD be used.
|
||||||
|
*/
|
||||||
|
private static final BitField ETO_NUMERICSLATIN = BitFieldFactory.getInstance(0x0800);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that both horizontal and vertical character displacement values
|
||||||
|
* SHOULD be provided.
|
||||||
|
*/
|
||||||
|
private static final BitField ETO_PDY = BitFieldFactory.getInstance(0x2000);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 16-bit signed integer that defines the y-coordinate, in logical units, where the
|
* A 16-bit signed integer that defines the y-coordinate, in logical units, where the
|
||||||
text string is to be located.
|
text string is to be located.
|
||||||
|
@ -199,39 +243,11 @@ public class HwmfText {
|
||||||
* A 16-bit signed integer that defines the length of the string.
|
* A 16-bit signed integer that defines the length of the string.
|
||||||
*/
|
*/
|
||||||
private int stringLength;
|
private int stringLength;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 16-bit unsigned integer that defines the use of the application-defined
|
* A 16-bit unsigned integer that defines the use of the application-defined
|
||||||
* rectangle. This member can be a combination of one or more values in the
|
* rectangle. This member can be a combination of one or more values in the
|
||||||
* ExtTextOutOptions Flags:
|
* ExtTextOutOptions Flags (ETO_*)
|
||||||
*
|
|
||||||
* ETO_OPAQUE (0x0002):
|
|
||||||
* Indicates that the background color that is defined in the playback device context
|
|
||||||
* SHOULD be used to fill the rectangle.
|
|
||||||
*
|
|
||||||
* ETO_CLIPPED (0x0004):
|
|
||||||
* Indicates that the text SHOULD be clipped to the rectangle.
|
|
||||||
*
|
|
||||||
* ETO_GLYPH_INDEX (0x0010):
|
|
||||||
* Indicates that the string to be output SHOULD NOT require further processing
|
|
||||||
* with respect to the placement of the characters, and an array of character
|
|
||||||
* placement values SHOULD be provided. This character placement process is
|
|
||||||
* useful for fonts in which diacritical characters affect character spacing.
|
|
||||||
*
|
|
||||||
* ETO_RTLREADING (0x0080):
|
|
||||||
* Indicates that the text MUST be laid out in right-to-left reading order, instead of
|
|
||||||
* the default left-to-right order. This SHOULD be applied only when the font that is
|
|
||||||
* defined in the playback device context is either Hebrew or Arabic. <37>
|
|
||||||
*
|
|
||||||
* ETO_NUMERICSLOCAL (0x0400):
|
|
||||||
* Indicates that to display numbers, digits appropriate to the locale SHOULD be
|
|
||||||
* used.
|
|
||||||
*
|
|
||||||
* ETO_NUMERICSLATIN (0x0800):
|
|
||||||
* Indicates that to display numbers, European digits SHOULD be used. <39>
|
|
||||||
*
|
|
||||||
* ETO_PDY (0x2000):
|
|
||||||
* Indicates that both horizontal and vertical character displacement values
|
|
||||||
* SHOULD be provided.
|
|
||||||
*/
|
*/
|
||||||
private int fwOpts;
|
private int fwOpts;
|
||||||
/**
|
/**
|
||||||
|
@ -275,7 +291,8 @@ public class HwmfText {
|
||||||
|
|
||||||
int size = 4*LittleEndianConsts.SHORT_SIZE;
|
int size = 4*LittleEndianConsts.SHORT_SIZE;
|
||||||
|
|
||||||
if (fwOpts != 0 && size+8<=remainingRecordSize) {
|
// Check if we have a rectangle
|
||||||
|
if ((ETO_OPAQUE.isSet(fwOpts) || ETO_CLIPPED.isSet(fwOpts)) && size+8<=remainingRecordSize) {
|
||||||
// the bounding rectangle is optional and only read when fwOpts are given
|
// the bounding rectangle is optional and only read when fwOpts are given
|
||||||
left = leis.readShort();
|
left = leis.readShort();
|
||||||
top = leis.readShort();
|
top = leis.readShort();
|
||||||
|
@ -289,16 +306,20 @@ public class HwmfText {
|
||||||
text = new String(buf, 0, stringLength, LocaleUtil.CHARSET_1252);
|
text = new String(buf, 0, stringLength, LocaleUtil.CHARSET_1252);
|
||||||
size += buf.length;
|
size += buf.length;
|
||||||
|
|
||||||
if (size < remainingRecordSize) {
|
if (size >= remainingRecordSize) {
|
||||||
if (size + stringLength*LittleEndianConsts.SHORT_SIZE < remainingRecordSize) {
|
logger.log(POILogger.INFO, "META_EXTTEXTOUT doesn't contain character tracking info");
|
||||||
throw new RecordFormatException("can't read Dx array - given recordSize doesn't contain enough values for string length "+stringLength);
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dxLen = Math.min(stringLength, (remainingRecordSize-size)/LittleEndianConsts.SHORT_SIZE);
|
||||||
|
if (dxLen < stringLength) {
|
||||||
|
logger.log(POILogger.WARN, "META_EXTTEXTOUT tracking info doesn't cover all characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
dx = new int[stringLength];
|
dx = new int[stringLength];
|
||||||
for (int i=0; i<dx.length; i++) {
|
for (int i=0; i<dxLen; i++) {
|
||||||
dx[i] = leis.readShort();
|
dx[i] = leis.readShort();
|
||||||
}
|
size += LittleEndianConsts.SHORT_SIZE;
|
||||||
size += dx.length*LittleEndianConsts.SHORT_SIZE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
|
@ -306,7 +327,8 @@ public class HwmfText {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
|
Rectangle2D bounds = new Rectangle2D.Double(x, y, 0, 0);
|
||||||
|
ctx.drawString(text, bounds, dx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -376,7 +376,7 @@ public class HwmfWindowing {
|
||||||
* The META_OFFSETCLIPRGN record moves the clipping region in the playback device context by the
|
* The META_OFFSETCLIPRGN record moves the clipping region in the playback device context by the
|
||||||
* specified offsets.
|
* specified offsets.
|
||||||
*/
|
*/
|
||||||
public static class WmfOffsetClipRgn implements HwmfRecord {
|
public static class WmfOffsetClipRgn implements HwmfRecord, HwmfObjectTableEntry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 16-bit signed integer that defines the number of logical units to move up or down.
|
* A 16-bit signed integer that defines the number of logical units to move up or down.
|
||||||
|
@ -402,7 +402,11 @@ public class HwmfWindowing {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,7 +414,7 @@ public class HwmfWindowing {
|
||||||
* The META_EXCLUDECLIPRECT record sets the clipping region in the playback device context to the
|
* The META_EXCLUDECLIPRECT record sets the clipping region in the playback device context to the
|
||||||
* existing clipping region minus the specified rectangle.
|
* existing clipping region minus the specified rectangle.
|
||||||
*/
|
*/
|
||||||
public static class WmfExcludeClipRect implements HwmfRecord {
|
public static class WmfExcludeClipRect implements HwmfRecord, HwmfObjectTableEntry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
|
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
|
||||||
|
@ -452,7 +456,11 @@ public class HwmfWindowing {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,7 +469,7 @@ public class HwmfWindowing {
|
||||||
* The META_INTERSECTCLIPRECT record sets the clipping region in the playback device context to the
|
* 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.
|
* intersection of the existing clipping region and the specified rectangle.
|
||||||
*/
|
*/
|
||||||
public static class WmfIntersectClipRect implements HwmfRecord {
|
public static class WmfIntersectClipRect implements HwmfRecord, HwmfObjectTableEntry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
|
* A 16-bit signed integer that defines the y-coordinate, in logical units, of the
|
||||||
|
@ -503,7 +511,11 @@ public class HwmfWindowing {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(HwmfGraphics ctx) {
|
public void draw(HwmfGraphics ctx) {
|
||||||
|
ctx.addObjectTableEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyObject(HwmfGraphics ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,8 @@ package org.apache.poi.hwmf.usermodel;
|
||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.GraphicsConfiguration;
|
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.geom.Rectangle2D.Double;
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -38,9 +36,13 @@ import org.apache.poi.hwmf.record.HwmfRecordType;
|
||||||
import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowExt;
|
import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowExt;
|
||||||
import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowOrg;
|
import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowOrg;
|
||||||
import org.apache.poi.util.LittleEndianInputStream;
|
import org.apache.poi.util.LittleEndianInputStream;
|
||||||
|
import org.apache.poi.util.POILogFactory;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
import org.apache.poi.util.Units;
|
import org.apache.poi.util.Units;
|
||||||
|
|
||||||
public class HwmfPicture {
|
public class HwmfPicture {
|
||||||
|
private static final POILogger logger = POILogFactory.getLogger(HwmfPicture.class);
|
||||||
|
|
||||||
final List<HwmfRecord> records = new ArrayList<HwmfRecord>();
|
final List<HwmfRecord> records = new ArrayList<HwmfRecord>();
|
||||||
final HwmfPlaceableHeader placeableHeader;
|
final HwmfPlaceableHeader placeableHeader;
|
||||||
final HwmfHeader header;
|
final HwmfHeader header;
|
||||||
|
@ -52,6 +54,10 @@ public class HwmfPicture {
|
||||||
header = new HwmfHeader(leis);
|
header = new HwmfHeader(leis);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
if (leis.available() < 6) {
|
||||||
|
logger.log(POILogger.ERROR, "unexpected eof - wmf file was truncated");
|
||||||
|
break;
|
||||||
|
}
|
||||||
// recordSize in DWORDs
|
// recordSize in DWORDs
|
||||||
long recordSize = leis.readUInt()*2;
|
long recordSize = leis.readUInt()*2;
|
||||||
int recordFunction = leis.readShort();
|
int recordFunction = leis.readShort();
|
||||||
|
|
|
@ -20,7 +20,10 @@ package org.apache.poi.hslf.model;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.awt.geom.*;
|
import java.awt.geom.Area;
|
||||||
|
import java.awt.geom.Line2D;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
|
||||||
import org.apache.poi.hslf.usermodel.HSLFFreeformShape;
|
import org.apache.poi.hslf.usermodel.HSLFFreeformShape;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -38,7 +41,7 @@ public final class TestFreeform {
|
||||||
@Test
|
@Test
|
||||||
public void testClosedPath() {
|
public void testClosedPath() {
|
||||||
|
|
||||||
GeneralPath path1 = new GeneralPath();
|
Path2D.Double path1 = new Path2D.Double();
|
||||||
path1.moveTo(100, 100);
|
path1.moveTo(100, 100);
|
||||||
path1.lineTo(200, 100);
|
path1.lineTo(200, 100);
|
||||||
path1.lineTo(200, 200);
|
path1.lineTo(200, 200);
|
||||||
|
@ -55,7 +58,7 @@ public final class TestFreeform {
|
||||||
@Test
|
@Test
|
||||||
public void testLine() {
|
public void testLine() {
|
||||||
|
|
||||||
GeneralPath path1 = new GeneralPath(new Line2D.Double(100, 100, 200, 100));
|
Path2D.Double path1 = new Path2D.Double(new Line2D.Double(100, 100, 200, 100));
|
||||||
|
|
||||||
HSLFFreeformShape p = new HSLFFreeformShape();
|
HSLFFreeformShape p = new HSLFFreeformShape();
|
||||||
p.setPath(path1);
|
p.setPath(path1);
|
||||||
|
@ -67,7 +70,7 @@ public final class TestFreeform {
|
||||||
@Test
|
@Test
|
||||||
public void testRectangle() {
|
public void testRectangle() {
|
||||||
|
|
||||||
GeneralPath path1 = new GeneralPath(new Rectangle2D.Double(100, 100, 200, 50));
|
Path2D.Double path1 = new Path2D.Double(new Rectangle2D.Double(100, 100, 200, 50));
|
||||||
|
|
||||||
HSLFFreeformShape p = new HSLFFreeformShape();
|
HSLFFreeformShape p = new HSLFFreeformShape();
|
||||||
p.setPath(path1);
|
p.setPath(path1);
|
||||||
|
@ -84,8 +87,8 @@ public final class TestFreeform {
|
||||||
public void test54188() {
|
public void test54188() {
|
||||||
|
|
||||||
HSLFFreeformShape p = new HSLFFreeformShape();
|
HSLFFreeformShape p = new HSLFFreeformShape();
|
||||||
GeneralPath path = p.getPath();
|
Path2D.Double path = p.getPath();
|
||||||
GeneralPath emptyPath = new GeneralPath();
|
Path2D.Double emptyPath = new Path2D.Double();
|
||||||
assertEquals(emptyPath.getBounds2D(), path.getBounds2D());
|
assertEquals(emptyPath.getBounds2D(), path.getBounds2D());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,23 +30,22 @@ import java.awt.Rectangle;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import org.apache.poi.POIDataSamples;
|
import org.apache.poi.POIDataSamples;
|
||||||
import org.apache.poi.ddf.EscherBSERecord;
|
import org.apache.poi.ddf.EscherBSERecord;
|
||||||
import org.apache.poi.hssf.usermodel.DummyGraphics2d;
|
import org.apache.poi.hssf.usermodel.DummyGraphics2d;
|
||||||
import org.apache.poi.sl.draw.Drawable;
|
import org.apache.poi.sl.draw.DrawFactory;
|
||||||
import org.apache.poi.sl.usermodel.PictureData.PictureType;
|
import org.apache.poi.sl.usermodel.PictureData.PictureType;
|
||||||
import org.apache.poi.sl.usermodel.Slide;
|
import org.apache.poi.sl.usermodel.Slide;
|
||||||
import org.apache.poi.sl.usermodel.SlideShow;
|
import org.apache.poi.sl.usermodel.SlideShow;
|
||||||
import org.apache.poi.util.JvmBugs;
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -64,7 +63,7 @@ public final class TestPicture {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void multiplePictures() throws Exception {
|
public void multiplePictures() throws IOException {
|
||||||
HSLFSlideShow ppt = new HSLFSlideShow();
|
HSLFSlideShow ppt = new HSLFSlideShow();
|
||||||
|
|
||||||
HSLFSlide s = ppt.createSlide();
|
HSLFSlide s = ppt.createSlide();
|
||||||
|
@ -92,6 +91,8 @@ public final class TestPicture {
|
||||||
EscherBSERecord bse3 = pict.getEscherBSERecord();
|
EscherBSERecord bse3 = pict.getEscherBSERecord();
|
||||||
assertSame(bse2, bse3);
|
assertSame(bse2, bse3);
|
||||||
assertEquals(3, bse1.getRef());
|
assertEquals(3, bse1.getRef());
|
||||||
|
|
||||||
|
ppt.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -99,7 +100,7 @@ public final class TestPicture {
|
||||||
* was not found. The correct behaviour is to return null.
|
* was not found. The correct behaviour is to return null.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void bug46122() {
|
public void bug46122() throws IOException {
|
||||||
HSLFSlideShow ppt = new HSLFSlideShow();
|
HSLFSlideShow ppt = new HSLFSlideShow();
|
||||||
HSLFSlide slide = ppt.createSlide();
|
HSLFSlide slide = ppt.createSlide();
|
||||||
HSLFPictureData pd = HSLFPictureData.create(PictureType.PNG);
|
HSLFPictureData pd = HSLFPictureData.create(PictureType.PNG);
|
||||||
|
@ -112,10 +113,12 @@ public final class TestPicture {
|
||||||
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
|
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
|
||||||
Graphics2D graphics = img.createGraphics();
|
Graphics2D graphics = img.createGraphics();
|
||||||
pict.draw(graphics);
|
pict.draw(graphics);
|
||||||
|
|
||||||
|
ppt.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void macImages() throws Exception {
|
public void macImages() throws IOException {
|
||||||
HSLFSlideShowImpl hss = new HSLFSlideShowImpl(_slTests.openResourceAsStream("53446.ppt"));
|
HSLFSlideShowImpl hss = new HSLFSlideShowImpl(_slTests.openResourceAsStream("53446.ppt"));
|
||||||
|
|
||||||
List<HSLFPictureData> pictures = hss.getPictureData();
|
List<HSLFPictureData> pictures = hss.getPictureData();
|
||||||
|
@ -154,11 +157,14 @@ public final class TestPicture {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hss.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("Just for visual validation - antialiasing is different on various systems")
|
@Ignore("Just for visual validation - antialiasing is different on various systems")
|
||||||
public void bug54541() throws Exception {
|
public void bug54541()
|
||||||
|
throws IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
|
||||||
String files[] = {
|
String files[] = {
|
||||||
// "sample_pptx_grouping_issues.pptx",
|
// "sample_pptx_grouping_issues.pptx",
|
||||||
// "54542_cropped_bitmap.pptx",
|
// "54542_cropped_bitmap.pptx",
|
||||||
|
@ -196,7 +202,7 @@ public final class TestPicture {
|
||||||
} else {
|
} else {
|
||||||
BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_ARGB);
|
BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_ARGB);
|
||||||
Graphics2D graphics = img.createGraphics();
|
Graphics2D graphics = img.createGraphics();
|
||||||
fixFonts(graphics);
|
DrawFactory.getInstance(graphics).fixFonts(graphics);
|
||||||
slide.draw(graphics);
|
slide.draw(graphics);
|
||||||
graphics.setColor(Color.BLACK);
|
graphics.setColor(Color.BLACK);
|
||||||
graphics.setStroke(new BasicStroke(1));
|
graphics.setStroke(new BasicStroke(1));
|
||||||
|
@ -204,16 +210,8 @@ public final class TestPicture {
|
||||||
ImageIO.write(img, "PNG", new File(file.replaceFirst(".pptx?", "-")+slideNo+".png"));
|
ImageIO.write(img, "PNG", new File(file.replaceFirst(".pptx?", "-")+slideNo+".png"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
ss.close();
|
||||||
private void fixFonts(Graphics2D graphics) {
|
}
|
||||||
if (!JvmBugs.hasLineBreakMeasurerBug()) return;
|
|
||||||
Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);
|
|
||||||
if (fontMap == null) fontMap = new HashMap<String,String>();
|
|
||||||
fontMap.put("Calibri", "Lucida Sans");
|
|
||||||
fontMap.put("Cambria", "Lucida Bright");
|
|
||||||
graphics.setRenderingHint(Drawable.FONT_MAP, fontMap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
|
@ -64,7 +65,7 @@ public class TestHwmfParsing {
|
||||||
@Ignore("This is work-in-progress and not a real unit test ...")
|
@Ignore("This is work-in-progress and not a real unit test ...")
|
||||||
public void paint() throws IOException {
|
public void paint() throws IOException {
|
||||||
File f = POIDataSamples.getSlideShowInstance().getFile("santa.wmf");
|
File f = POIDataSamples.getSlideShowInstance().getFile("santa.wmf");
|
||||||
// File f = new File("E:\\project\\poi\\misc\\govdocs-ppt", "000133-0001.wmf");
|
// File f = new File("bla.wmf");
|
||||||
FileInputStream fis = new FileInputStream(f);
|
FileInputStream fis = new FileInputStream(f);
|
||||||
HwmfPicture wmf = new HwmfPicture(fis);
|
HwmfPicture wmf = new HwmfPicture(fis);
|
||||||
fis.close();
|
fis.close();
|
||||||
|
@ -73,6 +74,11 @@ public class TestHwmfParsing {
|
||||||
int width = Units.pointsToPixel(dim.getWidth());
|
int width = Units.pointsToPixel(dim.getWidth());
|
||||||
// keep aspect ratio for height
|
// keep aspect ratio for height
|
||||||
int height = Units.pointsToPixel(dim.getHeight());
|
int height = Units.pointsToPixel(dim.getHeight());
|
||||||
|
double max = Math.max(width, height);
|
||||||
|
if (max > 1500) {
|
||||||
|
width *= 1500/max;
|
||||||
|
height *= 1500/max;
|
||||||
|
}
|
||||||
|
|
||||||
BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||||
Graphics2D g = bufImg.createGraphics();
|
Graphics2D g = bufImg.createGraphics();
|
||||||
|
@ -81,7 +87,7 @@ public class TestHwmfParsing {
|
||||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||||
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
||||||
|
|
||||||
wmf.draw(g);
|
wmf.draw(g, new Rectangle2D.Double(0,0,width,height));
|
||||||
|
|
||||||
g.dispose();
|
g.dispose();
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
|
@ -44,7 +44,7 @@ public class TestPresetGeometries {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
for(Path p : geom){
|
for(Path p : geom){
|
||||||
GeneralPath path = p.getPath(ctx);
|
Path2D path = p.getPath(ctx);
|
||||||
assertNotNull(path);
|
assertNotNull(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue