improved rendering of groupped shapes in xslf

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1210089 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2011-12-04 11:16:57 +00:00
parent bfcf5190eb
commit 62fa52d714
15 changed files with 130 additions and 78 deletions

View File

@ -102,7 +102,6 @@ class RenderableShape {
*/
public Paint selectPaint(Graphics2D graphics, XmlObject obj, CTSchemeColor phClr, PackagePart parentPart) {
XSLFTheme theme = _shape.getSheet().getTheme();
Rectangle2D anchor = _shape.getAnchor();
Paint paint = null;
if (obj instanceof CTNoFillProperties) {
@ -119,6 +118,7 @@ class RenderableShape {
paint = createTexturePaint(blipFill, graphics, parentPart);
}
else if (obj instanceof CTGradientFillProperties) {
Rectangle2D anchor = getAnchor(graphics);
CTGradientFillProperties gradFill = (CTGradientFillProperties) obj;
if (gradFill.isSetLin()) {
paint = createLinearGradientPaint(graphics, gradFill, anchor, theme, phClr);
@ -477,8 +477,6 @@ class RenderableShape {
float lineWidth = (float) _shape.getLineWidth();
if(lineWidth == 0.0f) lineWidth = 0.25f; // Both PowerPoint and OOo draw zero-length lines as 0.25pt
Number fontScale = (Number)graphics.getRenderingHint(XSLFRenderingHint.GROUP_SCALE);
if(fontScale != null) lineWidth *= fontScale.floatValue();
LineDash lineDash = _shape.getLineDash();
float[] dash = null;
@ -512,7 +510,7 @@ class RenderableShape {
}
public void render(Graphics2D graphics){
Collection<Outline> elems = computeOutlines();
Collection<Outline> elems = computeOutlines(graphics);
// shadow
XSLFShadow shadow = _shape.getShadow();
@ -549,7 +547,7 @@ class RenderableShape {
}
}
private Collection<Outline> computeOutlines() {
private Collection<Outline> computeOutlines(Graphics2D graphics) {
Collection<Outline> lst = new ArrayList<Outline>();
CustomGeometry geom = _shape.getGeometry();
@ -557,7 +555,7 @@ class RenderableShape {
return lst;
}
Rectangle2D anchor = _shape.getAnchor();
Rectangle2D anchor = getAnchor(graphics);
for (Path p : geom) {
double w = p.getW() == -1 ? anchor.getWidth() * Units.EMU_PER_POINT : p.getW();
@ -615,4 +613,16 @@ class RenderableShape {
return lst;
}
public Rectangle2D getAnchor(Graphics2D graphics) {
Rectangle2D anchor = _shape.getAnchor();
if(graphics == null) {
return anchor;
}
AffineTransform tx = (AffineTransform)graphics.getRenderingHint(XSLFRenderingHint.GROUP_TRANSFORM);
if(tx != null) {
anchor = tx.createTransformedShape(anchor).getBounds2D();
}
return anchor;
}
}

View File

@ -282,16 +282,15 @@ public class XSLFGroupShape extends XSLFShape {
// anchor of this group relative to the parent shape
Rectangle2D exterior = getAnchor();
graphics.translate(exterior.getX(), exterior.getY());
double scaleX = exterior.getWidth() / interior.getWidth();
double scaleY = exterior.getHeight() / interior.getHeight();
AffineTransform tx = (AffineTransform)graphics.getRenderingHint(XSLFRenderingHint.GROUP_TRANSFORM);
AffineTransform tx0 = new AffineTransform(tx);
// group transform scales shapes but not fonts
Number prevFontScale = (Number)graphics.getRenderingHint(XSLFRenderingHint.GROUP_SCALE);
graphics.setRenderingHint(XSLFRenderingHint.GROUP_SCALE, Math.abs(1/scaleY));
double scaleX = interior.getWidth() == 0. ? 1.0 : exterior.getWidth() / interior.getWidth();
double scaleY = interior.getHeight() == 0. ? 1.0 : exterior.getHeight() / interior.getHeight();
graphics.scale(scaleX, scaleY);
graphics.translate(-interior.getX(), -interior.getY());
tx.translate(exterior.getX(), exterior.getY());
tx.scale(scaleX, scaleY);
tx.translate(-interior.getX(), -interior.getY());
for (XSLFShape shape : getShapes()) {
// remember the initial transform and restore it after we are done with the drawing
@ -306,7 +305,7 @@ public class XSLFGroupShape extends XSLFShape {
graphics.setRenderingHint(XSLFRenderingHint.GRESTORE, true);
}
graphics.setRenderingHint(XSLFRenderingHint.GROUP_SCALE, prevFontScale);
graphics.setRenderingHint(XSLFRenderingHint.GROUP_TRANSFORM, tx0);
}

View File

@ -80,19 +80,14 @@ public class XSLFImageRendener {
Rectangle2D anchor) {
try {
BufferedImage img = ImageIO.read(data.getPackagePart().getInputStream());
Number groupScale = (Number)graphics.getRenderingHint(XSLFRenderingHint.GROUP_SCALE);
if(groupScale != null) {
double sx = anchor.getWidth()/img.getWidth();
double sy = anchor.getHeight()/img.getHeight();
double tx = anchor.getX();
double ty = anchor.getY();
AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ;
graphics.drawRenderedImage(img, at);
} else {
graphics.drawImage(img,
(int) anchor.getX(), (int) anchor.getY(),
(int) anchor.getWidth(), (int) anchor.getHeight(), null);
}
double sx = anchor.getWidth()/img.getWidth();
double sy = anchor.getHeight()/img.getHeight();
double tx = anchor.getX();
double ty = anchor.getY();
AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ;
graphics.drawRenderedImage(img, at);
return true;
} catch (Exception e) {
return false;

View File

@ -16,6 +16,7 @@
==================================================================== */
package org.apache.poi.xslf.usermodel;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.util.Beta;
@ -77,7 +78,12 @@ public final class XSLFNotes extends XSLFSheet {
}
@Override
public XSLFSheet getMasterSheet() {
public XSLFNotesMaster getMasterSheet() {
for (POIXMLDocumentPart p : getRelations()) {
if (p instanceof XSLFNotesMaster){
return (XSLFNotesMaster)p;
}
}
return null;
}
}

View File

@ -129,7 +129,10 @@ public class XSLFPictureShape extends XSLFSimpleShape {
XSLFImageRendener renderer = (XSLFImageRendener)graphics.getRenderingHint(XSLFRenderingHint.IMAGE_RENDERER);
if(renderer == null) renderer = new XSLFImageRendener();
renderer.drawImage(graphics, data, getAnchor());
RenderableShape rShape = new RenderableShape(this);
Rectangle2D anchor = rShape.getAnchor(graphics);
renderer.drawImage(graphics, data, anchor);
}

View File

@ -75,7 +75,7 @@ public class XSLFRenderingHint extends RenderingHints.Key {
public static final int TEXT_AS_SHAPES = 2;
@Internal
static final XSLFRenderingHint GROUP_SCALE = new XSLFRenderingHint(5);
static final XSLFRenderingHint GROUP_TRANSFORM = new XSLFRenderingHint(5);
/**
* Use this object to resolve unknown / missing fonts when rendering slides

View File

@ -24,6 +24,7 @@ import org.apache.poi.util.Internal;
import org.apache.xmlbeans.XmlObject;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
/**
@ -131,6 +132,10 @@ public abstract class XSLFShape {
*/
protected void applyTransform(Graphics2D graphics) {
Rectangle2D anchor = getAnchor();
AffineTransform tx = (AffineTransform)graphics.getRenderingHint(XSLFRenderingHint.GROUP_TRANSFORM);
if(tx != null) {
anchor = tx.createTransformedShape(anchor).getBounds2D();
}
// rotation
double rotation = getRotation();

View File

@ -430,6 +430,7 @@ public abstract class XSLFSheet extends POIXMLDocumentPart implements Iterable<X
XSLFSheet master = getMasterSheet();
if(getFollowMasterGraphics() && master != null) master.draw(graphics);
graphics.setRenderingHint(XSLFRenderingHint.GROUP_TRANSFORM, new AffineTransform());
for(XSLFShape shape : getShapeList()) {
if(!canDraw(shape)) continue;

View File

@ -544,7 +544,7 @@ public abstract class XSLFSimpleShape extends XSLFShape {
Color lineColor = getLineColor();
if(lineColor != null) {
graphics.setPaint(lineColor);
for(Outline o : getDecorationOutlines()){
for(Outline o : getDecorationOutlines(graphics)){
if(o.getPath().isFilled()){
graphics.fill(o.getOutline());
}
@ -820,13 +820,13 @@ public abstract class XSLFSimpleShape extends XSLFShape {
return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1];
}
Outline getTailDecoration() {
Outline getTailDecoration(Graphics2D graphics) {
LineEndLength tailLength = getLineTailLength();
LineEndWidth tailWidth = getLineTailWidth();
double lineWidth = Math.max(2.5, getLineWidth());
Rectangle2D anchor = getAnchor();
Rectangle2D anchor = new RenderableShape(this).getAnchor(graphics);
double x2 = anchor.getX() + anchor.getWidth(),
y2 = anchor.getY() + anchor.getHeight();
@ -879,12 +879,13 @@ public abstract class XSLFSimpleShape extends XSLFShape {
return shape == null ? null : new Outline(shape, p);
}
Outline getHeadDecoration() {
Outline getHeadDecoration(Graphics2D graphics) {
LineEndLength headLength = getLineHeadLength();
LineEndWidth headWidth = getLineHeadWidth();
double lineWidth = Math.max(2.5, getLineWidth());
Rectangle2D anchor = getAnchor();
Rectangle2D anchor = new RenderableShape(this).getAnchor(graphics);
double x1 = anchor.getX(),
y1 = anchor.getY();
@ -938,13 +939,13 @@ public abstract class XSLFSimpleShape extends XSLFShape {
return shape == null ? null : new Outline(shape, p);
}
private List<Outline> getDecorationOutlines(){
private List<Outline> getDecorationOutlines(Graphics2D graphics){
List<Outline> lst = new ArrayList<Outline>();
Outline head = getHeadDecoration();
Outline head = getHeadDecoration(graphics);
if(head != null) lst.add(head);
Outline tail = getTailDecoration();
Outline tail = getTailDecoration(graphics);
if(tail != null) lst.add(tail);
return lst;
}

View File

@ -19,10 +19,14 @@
package org.apache.poi.xslf.usermodel;
import org.apache.poi.POIXMLException;
import org.apache.poi.util.Internal;
import org.apache.poi.util.Units;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTDTable;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTable;
@ -50,13 +54,23 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow
/*package*/ XSLFTable(CTGraphicalObjectFrame shape, XSLFSheet sheet){
super(shape, sheet);
for(XmlObject obj : shape.getGraphic().getGraphicData().selectPath("*")){
if(obj instanceof CTTable){
_table = (CTTable)obj;
XmlObject[] rs = shape.getGraphic().getGraphicData()
.selectPath("declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' ./a:tbl");
if (rs.length == 0) {
throw new IllegalStateException("a:tbl element was not found in\n " + shape.getGraphic().getGraphicData());
}
// Pesky XmlBeans bug - see Bugzilla #49934
// it never happens when using the full ooxml-schemas jar but may happen with the abridged poi-ooxml-schemas
if(rs[0] instanceof XmlAnyTypeImpl){
try {
rs[0] = CTTable.Factory.parse(rs[0].toString());
}catch (XmlException e){
throw new POIXMLException(e);
}
}
if(_table == null) throw new IllegalStateException("CTTable element was not found");
_table = (CTTable) rs[0];
_rows = new ArrayList<XSLFTableRow>(_table.sizeOfTrArray());
for(CTTableRow row : _table.getTrList()) _rows.add(new XSLFTableRow(row, this));
}

View File

@ -633,12 +633,13 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
*
* @return wrapping width in points
*/
double getWrappingWidth(boolean firstLine){
double getWrappingWidth(boolean firstLine, Graphics2D graphics){
// internal margins for the text box
double leftInset = _shape.getLeftInset();
double rightInset = _shape.getRightInset();
Rectangle2D anchor = _shape.getAnchor();
RenderableShape rShape = new RenderableShape(_shape);
Rectangle2D anchor = rShape.getAnchor(graphics);
double leftMargin = getLeftMargin();
double indent = getIndent();
@ -667,7 +668,8 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
public double draw(Graphics2D graphics, double x, double y){
double leftInset = _shape.getLeftInset();
double rightInset = _shape.getRightInset();
Rectangle2D anchor = _shape.getAnchor();
RenderableShape rShape = new RenderableShape(_shape);
Rectangle2D anchor = rShape.getAnchor(graphics);
double penY = y;
double leftMargin = getLeftMargin();
@ -758,10 +760,8 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
string.addAttribute(TextAttribute.FAMILY, fontFamily, startIndex, endIndex);
float fontSz = (float)run.getFontSize();
Number fontScale = (Number)graphics.getRenderingHint(XSLFRenderingHint.GROUP_SCALE);
if(fontScale != null) fontSz *= fontScale.floatValue();
string.addAttribute(TextAttribute.SIZE, fontSz , startIndex, endIndex);
if(run.isBold()) {
string.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIndex, endIndex);
}
@ -827,7 +827,7 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
for (;;) {
int startIndex = measurer.getPosition();
double wrappingWidth = getWrappingWidth(_lines.size() == 0) + 1; // add a pixel to compensate rounding errors
double wrappingWidth = getWrappingWidth(_lines.size() == 0, graphics) + 1; // add a pixel to compensate rounding errors
// shape width can be smaller that the sum of insets (this was proved by a test file)
if(wrappingWidth < 0) wrappingWidth = 1;
@ -901,21 +901,23 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
CTTextParagraphProperties getDefaultMasterStyle(){
CTPlaceholder ph = _shape.getCTPlaceholder();
String defaultStyleSelector;
switch(ph.getType().intValue()){
case STPlaceholderType.INT_TITLE:
case STPlaceholderType.INT_CTR_TITLE:
defaultStyleSelector = "titleStyle";
break;
case STPlaceholderType.INT_FTR:
case STPlaceholderType.INT_SLD_NUM:
case STPlaceholderType.INT_DT:
defaultStyleSelector = "otherStyle";
break;
default:
defaultStyleSelector = "bodyStyle";
break;
if(ph == null) defaultStyleSelector = "otherStyle"; // no placeholder means plain text box
else {
switch(ph.getType().intValue()){
case STPlaceholderType.INT_TITLE:
case STPlaceholderType.INT_CTR_TITLE:
defaultStyleSelector = "titleStyle";
break;
case STPlaceholderType.INT_FTR:
case STPlaceholderType.INT_SLD_NUM:
case STPlaceholderType.INT_DT:
defaultStyleSelector = "otherStyle";
break;
default:
defaultStyleSelector = "bodyStyle";
break;
}
}
int level = getLevel();
// wind up and find the root master sheet which must be slide master
@ -951,7 +953,9 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
XMLSlideShow ppt = getParentShape().getSheet().getSlideShow();
CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getLevel());
if(themeProps != null) ok = visitor.fetch(themeProps);
} else {
}
if(!ok){
// defaults for placeholders are defined in the slide master
CTTextParagraphProperties defaultProps = getDefaultMasterStyle();
if(defaultProps != null) ok = visitor.fetch(defaultProps);

View File

@ -478,8 +478,8 @@ public class XSLFTextRun {
fetcher.isFetchingFromMaster = true;
ok = fetcher.fetch(themeProps);
}
} else {
// defaults for placeholders are defined in the slide master
}
if (!ok) {
CTTextParagraphProperties defaultProps = _p.getDefaultMasterStyle();
if(defaultProps != null) {
fetcher.isFetchingFromMaster = true;

View File

@ -469,7 +469,8 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<
public void drawContent(Graphics2D graphics) {
breakText(graphics);
Rectangle2D anchor = getAnchor();
RenderableShape rShape = new RenderableShape(this);
Rectangle2D anchor = rShape.getAnchor(graphics);
double x = anchor.getX() + getLeftInset();
double y = anchor.getY();

View File

@ -62,5 +62,18 @@ public class TestXSLFTextBox extends TestCase {
pPr.getLatin().setTypeface("Arial");
assertEquals(9.0, r.getFontSize());
assertEquals("Arial", r.getFontFamily());
// unset font size in presentation.xml. The value should be taken from master slide
// from /p:sldMaster/p:txStyles/p:otherStyle/a:lvl1pPr/a:defRPr
ppt.getCTPresentation().getDefaultTextStyle().getLvl1PPr().getDefRPr().unsetSz();
pPr = slide.getSlideMaster().getXmlObject().getTxStyles().getOtherStyle().getLvl1PPr().getDefRPr();
assertEquals(1800, pPr.getSz());
assertEquals(18.0, r.getFontSize());
pPr.setSz(2000);
assertEquals(20.0, r.getFontSize());
pPr.unsetSz(); // Should never be
assertEquals(-1.0, r.getFontSize());
}
}

View File

@ -51,16 +51,16 @@ public class TestXSLFTextParagraph extends TestCase {
// Case 1: bullet=false, leftMargin=0, indent=0.
expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
assertEquals(285.6, expectedWidth); // 300 - 7.2 - 7.2 - 0
assertEquals(expectedWidth, p.getWrappingWidth(true));
assertEquals(expectedWidth, p.getWrappingWidth(false));
assertEquals(expectedWidth, p.getWrappingWidth(true, null));
assertEquals(expectedWidth, p.getWrappingWidth(false, null));
p.setLeftMargin(36); // 0.5"
leftMargin = p.getLeftMargin();
assertEquals(36.0, leftMargin);
expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
assertEquals(249.6, expectedWidth, 1E-5); // 300 - 7.2 - 7.2 - 36
assertEquals(expectedWidth, p.getWrappingWidth(true));
assertEquals(expectedWidth, p.getWrappingWidth(false));
assertEquals(expectedWidth, p.getWrappingWidth(true, null));
assertEquals(expectedWidth, p.getWrappingWidth(false, null));
// increase insets, the wrapping width should get smaller
sh.setLeftInset(10);
@ -71,8 +71,8 @@ public class TestXSLFTextParagraph extends TestCase {
assertEquals(10.0, rightInset);
expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
assertEquals(244.0, expectedWidth); // 300 - 10 - 10 - 36
assertEquals(expectedWidth, p.getWrappingWidth(true));
assertEquals(expectedWidth, p.getWrappingWidth(false));
assertEquals(expectedWidth, p.getWrappingWidth(true, null));
assertEquals(expectedWidth, p.getWrappingWidth(false, null));
// set a positive indent of a 0.5 inch. This means "First Line" indentation:
// |<--- indent -->|Here goes first line of the text
@ -83,11 +83,11 @@ public class TestXSLFTextParagraph extends TestCase {
assertEquals(36.0, indent);
expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin - indent;
assertEquals(208.0, expectedWidth); // 300 - 10 - 10 - 36 - 6.4
assertEquals(expectedWidth, p.getWrappingWidth(true)); // first line is indented
assertEquals(expectedWidth, p.getWrappingWidth(true, null)); // first line is indented
// other lines are not indented
expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
assertEquals(244.0, expectedWidth); // 300 - 10 - 10 - 36
assertEquals(expectedWidth, p.getWrappingWidth(false));
assertEquals(expectedWidth, p.getWrappingWidth(false, null));
// set a negative indent of a 1 inch. This means "Hanging" indentation:
// Here goes first line of the text
@ -97,11 +97,11 @@ public class TestXSLFTextParagraph extends TestCase {
assertEquals(-72.0, indent);
expectedWidth = anchor.getWidth() - leftInset - rightInset;
assertEquals(280.0, expectedWidth); // 300 - 10 - 10
assertEquals(expectedWidth, p.getWrappingWidth(true)); // first line is NOT indented
assertEquals(expectedWidth, p.getWrappingWidth(true, null)); // first line is NOT indented
// other lines are indented by leftMargin (the value of indent is not used)
expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
assertEquals(244.0, expectedWidth); // 300 - 10 - 10 - 36
assertEquals(expectedWidth, p.getWrappingWidth(false));
assertEquals(expectedWidth, p.getWrappingWidth(false, null));
}
/**