more improvements in slide rendering

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@649911 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2008-04-20 11:17:48 +00:00
parent 383ac9d4d8
commit 40c49400e7
10 changed files with 229 additions and 72 deletions

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -19,6 +18,14 @@
package org.apache.poi.hslf.model; package org.apache.poi.hslf.model;
import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.hslf.usermodel.PictureData;
import org.apache.poi.hslf.blip.Bitmap;
import org.apache.poi.util.POILogger;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
/** /**
* Background shape * Background shape
@ -27,12 +34,42 @@ import org.apache.poi.ddf.EscherContainerRecord;
*/ */
public class Background extends Shape { public class Background extends Shape {
protected Background(EscherContainerRecord escherRecord, Shape parent){ protected Background(EscherContainerRecord escherRecord, Shape parent) {
super(escherRecord, parent); super(escherRecord, parent);
} }
protected EscherContainerRecord createSpContainer(boolean isChild){ protected EscherContainerRecord createSpContainer(boolean isChild) {
return null; return null;
} }
public void draw(Graphics2D graphics) {
Fill f = getFill();
Dimension pg = getSheet().getSlideShow().getPageSize();
Rectangle anchor = new Rectangle(0, 0, pg.width, pg.height);
switch (f.getFillType()) {
case Fill.FILL_SOLID:
Color color = f.getForegroundColor();
graphics.setPaint(color);
graphics.fill(anchor);
break;
case Fill.FILL_PICTURE:
PictureData data = f.getPictureData();
if (data instanceof Bitmap) {
BufferedImage img = null;
try {
img = ImageIO.read(new ByteArrayInputStream(data.getData()));
} catch (Exception e) {
logger.log(POILogger.WARN, "ImageIO failed to create image. image.type: " + data.getType());
return;
}
Image scaledImg = img.getScaledInstance(anchor.width, anchor.height, Image.SCALE_SMOOTH);
graphics.drawImage(scaledImg, anchor.x, anchor.y, null);
}
break;
default:
logger.log(POILogger.WARN, "unsuported fill type: " + f.getFillType());
break;
}
}
} }

View File

@ -21,6 +21,7 @@ import org.apache.poi.hslf.usermodel.PictureData;
import org.apache.poi.hslf.usermodel.SlideShow; import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hslf.record.Document; import org.apache.poi.hslf.record.Document;
import org.apache.poi.hslf.blip.Bitmap; import org.apache.poi.hslf.blip.Bitmap;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@ -29,6 +30,7 @@ import java.awt.*;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List; import java.util.List;
import java.util.Arrays; import java.util.Arrays;
@ -129,7 +131,7 @@ public class Picture extends SimpleShape {
//set default properties for a picture //set default properties for a picture
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 8388736); setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x800080);
//another weird feature of powerpoint: for picture id we must add 0x4000. //another weird feature of powerpoint: for picture id we must add 0x4000.
setEscherProperty(opt, (short)(EscherProperties.BLIP__BLIPTODISPLAY + 0x4000), idx); setEscherProperty(opt, (short)(EscherProperties.BLIP__BLIPTODISPLAY + 0x4000), idx);
@ -193,6 +195,43 @@ public class Picture extends SimpleShape {
return null; return null;
} }
/**
* Name of this picture.
*
* @return name of this picture
*/
public String getPictureName(){
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
EscherComplexProperty prop = (EscherComplexProperty)getEscherProperty(opt, EscherProperties.BLIP__BLIPFILENAME);
String name = null;
if(prop != null){
try {
name = new String(prop.getComplexData(), "UTF-16LE");
int idx = name.indexOf('\u0000');
return idx == -1 ? name : name.substring(0, idx);
} catch (UnsupportedEncodingException e){
throw new HSLFException(e);
}
}
return name;
}
/**
* Name of this picture.
*
* @param name of this picture
*/
public void setPictureName(String name){
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
try {
byte[] data = (name + '\u0000').getBytes("UTF-16LE");
EscherComplexProperty prop = new EscherComplexProperty(EscherProperties.BLIP__BLIPFILENAME, false, data);
opt.addEscherProperty(prop);
} catch (UnsupportedEncodingException e){
throw new HSLFException(e);
}
}
/** /**
* By default set the orininal image size * By default set the orininal image size
*/ */
@ -219,7 +258,7 @@ public class Picture extends SimpleShape {
Image scaledImg = img.getScaledInstance(anchor.width, anchor.height, Image.SCALE_SMOOTH); Image scaledImg = img.getScaledInstance(anchor.width, anchor.height, Image.SCALE_SMOOTH);
graphics.drawImage(scaledImg, anchor.x, anchor.y, null); graphics.drawImage(scaledImg, anchor.x, anchor.y, null);
} else { } else {
logger.log(POILogger.WARN, "Rendering of metafiles is not yet supported. image.type: " + data.getType()); logger.log(POILogger.WARN, "Rendering of metafiles is not yet supported. image.type: " + (data == null ? "NA" : data.getClass().getName()));
} }
} }
} }

View File

@ -166,12 +166,24 @@ public abstract class Shape {
if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ if ((flags & EscherSpRecord.FLAG_CHILD) != 0){
EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID);
anchor = new java.awt.Rectangle(); anchor = new java.awt.Rectangle();
anchor = new Rectangle2D.Float( if(rec == null){
(float)rec.getDx1()*POINT_DPI/MASTER_DPI, logger.log(POILogger.WARN, "EscherSpRecord.FLAG_CHILD is set but EscherChildAnchorRecord was not found");
(float)rec.getDy1()*POINT_DPI/MASTER_DPI, EscherClientAnchorRecord clrec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID);
(float)(rec.getDx2()-rec.getDx1())*POINT_DPI/MASTER_DPI, anchor = new java.awt.Rectangle();
(float)(rec.getDy2()-rec.getDy1())*POINT_DPI/MASTER_DPI anchor = new Rectangle2D.Float(
); (float)clrec.getCol1()*POINT_DPI/MASTER_DPI,
(float)clrec.getFlag()*POINT_DPI/MASTER_DPI,
(float)(clrec.getDx1()-clrec.getCol1())*POINT_DPI/MASTER_DPI,
(float)(clrec.getRow1()-clrec.getFlag())*POINT_DPI/MASTER_DPI
);
} else {
anchor = new Rectangle2D.Float(
(float)rec.getDx1()*POINT_DPI/MASTER_DPI,
(float)rec.getDy1()*POINT_DPI/MASTER_DPI,
(float)(rec.getDx2()-rec.getDx1())*POINT_DPI/MASTER_DPI,
(float)(rec.getDy2()-rec.getDy1())*POINT_DPI/MASTER_DPI
);
}
} }
else { else {
EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID);
@ -245,7 +257,7 @@ public abstract class Shape {
* @return escher property or <code>null</code> if not found. * @return escher property or <code>null</code> if not found.
*/ */
public static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){ public static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){
for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); ) if(opt != null) for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); )
{ {
EscherProperty prop = (EscherProperty) iterator.next(); EscherProperty prop = (EscherProperty) iterator.next();
if (prop.getPropertyNumber() == propId) if (prop.getPropertyNumber() == propId)

View File

@ -236,10 +236,21 @@ public class ShapeGroup extends Shape{
EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChildRecords().get(0); EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChildRecords().get(0);
EscherClientAnchorRecord clientAnchor = (EscherClientAnchorRecord)getEscherChild(spContainer, EscherClientAnchorRecord.RECORD_ID); EscherClientAnchorRecord clientAnchor = (EscherClientAnchorRecord)getEscherChild(spContainer, EscherClientAnchorRecord.RECORD_ID);
Rectangle2D.Float anchor = new Rectangle2D.Float(); Rectangle2D.Float anchor = new Rectangle2D.Float();
anchor.x = (float)clientAnchor.getCol1()*POINT_DPI/MASTER_DPI; if(clientAnchor == null){
anchor.y = (float)clientAnchor.getFlag()*POINT_DPI/MASTER_DPI; logger.log(POILogger.WARN, "EscherClientAnchorRecord was not found for the shape group");
anchor.width = (float)(clientAnchor.getDx1() - clientAnchor.getCol1())*POINT_DPI/MASTER_DPI ; EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(spContainer, EscherChildAnchorRecord.RECORD_ID);
anchor.height = (float)(clientAnchor.getRow1() - clientAnchor.getFlag())*POINT_DPI/MASTER_DPI; anchor = new Rectangle2D.Float(
(float)rec.getDx1()*POINT_DPI/MASTER_DPI,
(float)rec.getDy1()*POINT_DPI/MASTER_DPI,
(float)(rec.getDx2()-rec.getDx1())*POINT_DPI/MASTER_DPI,
(float)(rec.getDy2()-rec.getDy1())*POINT_DPI/MASTER_DPI
);
} else {
anchor.x = (float)clientAnchor.getCol1()*POINT_DPI/MASTER_DPI;
anchor.y = (float)clientAnchor.getFlag()*POINT_DPI/MASTER_DPI;
anchor.width = (float)(clientAnchor.getDx1() - clientAnchor.getCol1())*POINT_DPI/MASTER_DPI ;
anchor.height = (float)(clientAnchor.getRow1() - clientAnchor.getFlag())*POINT_DPI/MASTER_DPI;
}
return anchor; return anchor;
} }

View File

@ -17,6 +17,9 @@
package org.apache.poi.hslf.model; package org.apache.poi.hslf.model;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.POILogFactory;
import java.awt.*; import java.awt.*;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
@ -26,6 +29,7 @@ import java.awt.geom.Rectangle2D;
* @author Yegor Kozlov * @author Yegor Kozlov
*/ */
public class ShapePainter { public class ShapePainter {
protected static POILogger logger = POILogFactory.getLogger(ShapePainter.class);
public static void paint(SimpleShape shape, Graphics2D graphics){ public static void paint(SimpleShape shape, Graphics2D graphics){
Rectangle2D anchor = shape.getAnchor2D(); Rectangle2D anchor = shape.getAnchor2D();
@ -59,6 +63,7 @@ public class ShapePainter {
//fill //fill
Color fillColor = shape.getFill().getForegroundColor(); Color fillColor = shape.getFill().getForegroundColor();
if (fillColor != null) { if (fillColor != null) {
//TODO: implement gradient and texture fill patterns
graphics.setPaint(fillColor); graphics.setPaint(fillColor);
graphics.fill(outline); graphics.fill(outline);
} }
@ -68,12 +73,24 @@ public class ShapePainter {
if (lineColor != null){ if (lineColor != null){
graphics.setPaint(lineColor); graphics.setPaint(lineColor);
float width = (float)shape.getLineWidth(); float width = (float)shape.getLineWidth();
if(width == 0) width = 0.75f;
int dashing = shape.getLineDashing(); int dashing = shape.getLineDashing();
//TODO: implement more dashing styles //TODO: implement more dashing styles
float[] dashptrn = null; float[] dashptrn = null;
switch(dashing){ switch(dashing){
case Line.PEN_SOLID:
dashptrn = null;
break;
case Line.PEN_PS_DASH: case Line.PEN_PS_DASH:
dashptrn = new float[]{2, 2}; dashptrn = new float[]{width, width};
break;
case Line.PEN_DOTGEL:
dashptrn = new float[]{width*4, width*3};
break;
default:
logger.log(POILogger.WARN, "unsupported dashing: " + dashing);
dashptrn = new float[]{width, width};
break; break;
} }

View File

@ -101,8 +101,6 @@ public class SlideMaster extends MasterSheet {
default: default:
return null; return null;
} }
return null;
} }
prop = getStyleAttribute(txtype, level, name, isCharacter); prop = getStyleAttribute(txtype, level, name, isCharacter);
} }

View File

@ -44,6 +44,9 @@ public class TextPainter {
_shape = shape; _shape = shape;
} }
/**
* Convert the underlying set of rich text runs into java.text.AttributedString
*/
public AttributedString getAttributedString(TextRun txrun){ public AttributedString getAttributedString(TextRun txrun){
String text = txrun.getText(); String text = txrun.getText();
AttributedString at = new AttributedString(text); AttributedString at = new AttributedString(text);
@ -70,16 +73,6 @@ public class TextPainter {
return at; return at;
} }
protected RichTextRun getRichTextRunAt(int pos){
RichTextRun[] rt = _shape.getTextRun().getRichTextRuns();
for (int i = 0; i < rt.length; i++) {
int start = rt[i].getStartIndex();
int end = rt[i].getEndIndex();
if(pos >= start && pos < end) return rt[i];
}
return null;
}
public void paint(Graphics2D graphics){ public void paint(Graphics2D graphics){
TextRun run = _shape.getTextRun(); TextRun run = _shape.getTextRun();
if (run == null) return; if (run == null) return;
@ -106,7 +99,7 @@ public class TextPainter {
boolean prStart = text.charAt(startIndex) == '\n'; boolean prStart = text.charAt(startIndex) == '\n';
if(prStart) measurer.setPosition(startIndex++); if(prStart) measurer.setPosition(startIndex++);
RichTextRun rt = getRichTextRunAt(startIndex); RichTextRun rt = run.getRichTextRunAt(startIndex == text.length() ? (startIndex-1) : startIndex);
if(rt == null) { if(rt == null) {
logger.log(POILogger.WARN, "RichTextRun not found at pos" + startIndex + "; text.length: " + text.length()); logger.log(POILogger.WARN, "RichTextRun not found at pos" + startIndex + "; text.length: " + text.length());
break; break;
@ -133,33 +126,58 @@ public class TextPainter {
} }
int endIndex = measurer.getPosition(); int endIndex = measurer.getPosition();
float lineHeight = (float)textLayout.getBounds().getHeight();
int linespacing = rt.getLineSpacing();
if(linespacing == 0) linespacing = 100;
TextElement el = new TextElement(); TextElement el = new TextElement();
el.ascent = textLayout.getAscent(); if(linespacing >= 0){
el._startIndex = startIndex; el.ascent = textLayout.getAscent()*linespacing/100;
el._endIndex = endIndex; } else {
el.ascent = -linespacing*Shape.POINT_DPI/Shape.MASTER_DPI;
}
el._align = rt.getAlignment(); el._align = rt.getAlignment();
el._text = textLayout; el._text = textLayout;
el._textOffset = rt.getTextOffset(); el._textOffset = rt.getTextOffset();
textHeight += textLayout.getAscent(); if (prStart){
if (prStart || startIndex == 0){ int sp = rt.getSpaceBefore();
int spaceBefore = rt.getSpaceBefore(); float spaceBefore;
if (spaceBefore != 0) { if(sp >= 0){
float val = (float)(textLayout.getAscent() + textLayout.getDescent())* spaceBefore/100; spaceBefore = lineHeight * sp/100;
textHeight += val; } else {
el.ascent += val; spaceBefore = -sp*Shape.POINT_DPI/Shape.MASTER_DPI;
} }
el.ascent += spaceBefore;
} }
float descent;
if(linespacing >= 0){
descent = (textLayout.getDescent() + textLayout.getLeading())*linespacing/100;
} else {
descent = -linespacing*Shape.POINT_DPI/Shape.MASTER_DPI;
}
if (prStart){
int sp = rt.getSpaceAfter();
float spaceAfter;
if(sp >= 0){
spaceAfter = lineHeight * sp/100;
} else {
spaceAfter = -sp*Shape.POINT_DPI/Shape.MASTER_DPI;
}
el.ascent += spaceAfter;
}
el.descent = descent;
textHeight += el.ascent + el.descent;
if(rt.isBullet() && (prStart || startIndex == 0)){ if(rt.isBullet() && (prStart || startIndex == 0)){
it.setIndex(startIndex); it.setIndex(startIndex);
AttributedString bat = new AttributedString(Character.toString(rt.getBulletChar()), it.getAttributes()); AttributedString bat = new AttributedString(Character.toString(rt.getBulletChar()), it.getAttributes());
int bulletSize = rt.getBulletSize(); Color clr = rt.getBulletColor();
if (bulletSize != -1){ if (clr != null) bat.addAttribute(TextAttribute.FOREGROUND, clr);
Float sz = (Float)bat.getIterator().getAttribute(TextAttribute.SIZE);
if(sz != null) bat.addAttribute(TextAttribute.SIZE, new Float(sz.floatValue()*bulletSize/100));
}
TextLayout bulletLayout = new TextLayout(bat.getIterator(), graphics.getFontRenderContext()); TextLayout bulletLayout = new TextLayout(bat.getIterator(), graphics.getFontRenderContext());
if(text.substring(startIndex, endIndex).length() > 1){ if(text.substring(startIndex, endIndex).length() > 1){
@ -167,24 +185,6 @@ public class TextPainter {
el._bulletOffset = rt.getBulletOffset(); el._bulletOffset = rt.getBulletOffset();
} }
} }
float descent = textLayout.getDescent();
int lineSpacing = rt.getLineSpacing();
if(lineSpacing != 0) descent += textLayout.getLeading()*lineSpacing/100;
else descent = textLayout.getLeading();
textHeight += descent;
el.descent = descent;
if (prStart){
int spaceAfter = rt.getSpaceAfter();
if (spaceAfter != 0) {
float val = (float)(textLayout.getAscent() + textLayout.getDescent())* spaceAfter/100;
textHeight += val;
el.descent += val;
}
}
lines.add(el); lines.add(el);
} }
@ -242,9 +242,6 @@ public class TextPainter {
public TextLayout _bullet; public TextLayout _bullet;
public int _bulletOffset; public int _bulletOffset;
public int _align; public int _align;
public int _startIndex;
public int _endIndex;
public float _spacing;
public float ascent, descent; public float ascent, descent;
} }
} }

View File

@ -639,4 +639,20 @@ public class TextRun
public Hyperlink[] getHyperlinks(){ public Hyperlink[] getHyperlinks(){
return Hyperlink.find(this); return Hyperlink.find(this);
} }
/**
* Fetch RichTextRun at a given position
*
* @param pos 0-based index in the text
* @return RichTextRun or null if not found
*/
public RichTextRun getRichTextRunAt(int pos){
for (int i = 0; i < _rtRuns.length; i++) {
int start = _rtRuns[i].getStartIndex();
int end = _rtRuns[i].getEndIndex();
if(pos >= start && pos < end) return _rtRuns[i];
}
return null;
}
} }

View File

@ -642,13 +642,14 @@ public class RichTextRun {
* Returns the bullet color * Returns the bullet color
*/ */
public Color getBulletColor() { public Color getBulletColor() {
int rgb = getCharTextPropVal("bullet.color"); int rgb = getParaTextPropVal("bullet.color");
if (rgb >= 0x8000000) { if(rgb == -1) return getFontColor();
int idx = rgb % 0x8000000;
ColorSchemeAtom ca = parentRun.getSheet().getColorScheme();
if(idx >= 0 && idx <= 7) rgb = ca.getColor(idx);
}
int cidx = rgb >> 24;
if (rgb % 0x1000000 == 0){
ColorSchemeAtom ca = parentRun.getSheet().getColorScheme();
if(cidx >= 0 && cidx <= 7) rgb = ca.getColor(cidx);
}
Color tmp = new Color(rgb, true); Color tmp = new Color(rgb, true);
return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed()); return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());
} }

View File

@ -437,4 +437,33 @@ public class TestPictures extends TestCase{
assertTrue(pdata instanceof WMF); assertTrue(pdata instanceof WMF);
assertEquals(Picture.WMF, pdata.getType()); assertEquals(Picture.WMF, pdata.getType());
} }
public void testGetPictureName() throws Exception {
SlideShow ppt = new SlideShow(new HSLFSlideShow(new File(cwd, "ppt_with_png.ppt").getPath()));
Slide slide = ppt.getSlides()[0];
Picture p = (Picture)slide.getShapes()[0]; //the first slide contains JPEG
assertEquals("test", p.getPictureName());
}
public void testSetPictureName() throws Exception {
SlideShow ppt = new SlideShow();
Slide slide = ppt.createSlide();
File img = new File(cwd, "tomcat.png");
int idx = ppt.addPicture(img, Picture.PNG);
Picture pict = new Picture(idx);
pict.setPictureName("tomcat.png");
slide.addShape(pict);
//serialize and read again
ByteArrayOutputStream out = new ByteArrayOutputStream();
ppt.write(out);
out.close();
ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray()));
Picture p = (Picture)ppt.getSlides()[0].getShapes()[0];
assertEquals("tomcat.png", p.getPictureName());
}
} }