From 40c49400e7b800fdcb4efd55557b1f609ec91476 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Sun, 20 Apr 2008 11:17:48 +0000 Subject: [PATCH] more improvements in slide rendering git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@649911 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/hslf/model/Background.java | 43 ++++++++- .../org/apache/poi/hslf/model/Picture.java | 43 ++++++++- .../src/org/apache/poi/hslf/model/Shape.java | 26 ++++-- .../org/apache/poi/hslf/model/ShapeGroup.java | 19 +++- .../apache/poi/hslf/model/ShapePainter.java | 19 +++- .../apache/poi/hslf/model/SlideMaster.java | 2 - .../apache/poi/hslf/model/TextPainter.java | 91 +++++++++---------- .../org/apache/poi/hslf/model/TextRun.java | 16 ++++ .../poi/hslf/usermodel/RichTextRun.java | 13 +-- .../poi/hslf/usermodel/TestPictures.java | 29 ++++++ 10 files changed, 229 insertions(+), 72 deletions(-) diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Background.java b/src/scratchpad/src/org/apache/poi/hslf/model/Background.java index 4906922a8..e2718f6ac 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Background.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Background.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -19,6 +18,14 @@ package org.apache.poi.hslf.model; 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 @@ -27,12 +34,42 @@ import org.apache.poi.ddf.EscherContainerRecord; */ public class Background extends Shape { - protected Background(EscherContainerRecord escherRecord, Shape parent){ + protected Background(EscherContainerRecord escherRecord, Shape parent) { super(escherRecord, parent); } - protected EscherContainerRecord createSpContainer(boolean isChild){ + protected EscherContainerRecord createSpContainer(boolean isChild) { 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; + } + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java index ed642791f..e10986966 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java @@ -21,6 +21,7 @@ import org.apache.poi.hslf.usermodel.PictureData; import org.apache.poi.hslf.usermodel.SlideShow; import org.apache.poi.hslf.record.Document; import org.apache.poi.hslf.blip.Bitmap; +import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.util.POILogger; import javax.imageio.ImageIO; @@ -29,6 +30,7 @@ import java.awt.*; import java.awt.geom.Rectangle2D; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.util.List; import java.util.Arrays; @@ -129,7 +131,7 @@ public class Picture extends SimpleShape { //set default properties for a picture 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. setEscherProperty(opt, (short)(EscherProperties.BLIP__BLIPTODISPLAY + 0x4000), idx); @@ -193,6 +195,43 @@ public class Picture extends SimpleShape { 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 */ @@ -219,7 +258,7 @@ public class Picture extends SimpleShape { Image scaledImg = img.getScaledInstance(anchor.width, anchor.height, Image.SCALE_SMOOTH); graphics.drawImage(scaledImg, anchor.x, anchor.y, null); } 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())); } } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java index 2aed43f47..d4975df42 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java @@ -166,12 +166,24 @@ public abstract class Shape { if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); anchor = new java.awt.Rectangle(); - 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 - ); + if(rec == null){ + logger.log(POILogger.WARN, "EscherSpRecord.FLAG_CHILD is set but EscherChildAnchorRecord was not found"); + EscherClientAnchorRecord clrec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); + anchor = new java.awt.Rectangle(); + 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 { EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); @@ -245,7 +257,7 @@ public abstract class Shape { * @return escher property or null if not found. */ 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(); if (prop.getPropertyNumber() == propId) diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java index 2c146817e..a0fbb114d 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java @@ -236,10 +236,21 @@ public class ShapeGroup extends Shape{ EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChildRecords().get(0); EscherClientAnchorRecord clientAnchor = (EscherClientAnchorRecord)getEscherChild(spContainer, EscherClientAnchorRecord.RECORD_ID); Rectangle2D.Float anchor = new Rectangle2D.Float(); - 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; + if(clientAnchor == null){ + logger.log(POILogger.WARN, "EscherClientAnchorRecord was not found for the shape group"); + EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(spContainer, EscherChildAnchorRecord.RECORD_ID); + 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; } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapePainter.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapePainter.java index 4e3054569..b50b4f7ca 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapePainter.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapePainter.java @@ -17,6 +17,9 @@ package org.apache.poi.hslf.model; +import org.apache.poi.util.POILogger; +import org.apache.poi.util.POILogFactory; + import java.awt.*; import java.awt.geom.Rectangle2D; @@ -26,6 +29,7 @@ import java.awt.geom.Rectangle2D; * @author Yegor Kozlov */ public class ShapePainter { + protected static POILogger logger = POILogFactory.getLogger(ShapePainter.class); public static void paint(SimpleShape shape, Graphics2D graphics){ Rectangle2D anchor = shape.getAnchor2D(); @@ -59,6 +63,7 @@ public class ShapePainter { //fill Color fillColor = shape.getFill().getForegroundColor(); if (fillColor != null) { + //TODO: implement gradient and texture fill patterns graphics.setPaint(fillColor); graphics.fill(outline); } @@ -68,12 +73,24 @@ public class ShapePainter { if (lineColor != null){ graphics.setPaint(lineColor); float width = (float)shape.getLineWidth(); + if(width == 0) width = 0.75f; + int dashing = shape.getLineDashing(); //TODO: implement more dashing styles float[] dashptrn = null; switch(dashing){ + case Line.PEN_SOLID: + dashptrn = null; + break; 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; } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/SlideMaster.java b/src/scratchpad/src/org/apache/poi/hslf/model/SlideMaster.java index 2a8467437..26870dbdb 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/SlideMaster.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/SlideMaster.java @@ -101,8 +101,6 @@ public class SlideMaster extends MasterSheet { default: return null; } - return null; - } prop = getStyleAttribute(txtype, level, name, isCharacter); } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java index c4eac3d06..7d23c7cfb 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java @@ -44,6 +44,9 @@ public class TextPainter { _shape = shape; } + /** + * Convert the underlying set of rich text runs into java.text.AttributedString + */ public AttributedString getAttributedString(TextRun txrun){ String text = txrun.getText(); AttributedString at = new AttributedString(text); @@ -70,16 +73,6 @@ public class TextPainter { 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){ TextRun run = _shape.getTextRun(); if (run == null) return; @@ -106,7 +99,7 @@ public class TextPainter { boolean prStart = text.charAt(startIndex) == '\n'; if(prStart) measurer.setPosition(startIndex++); - RichTextRun rt = getRichTextRunAt(startIndex); + RichTextRun rt = run.getRichTextRunAt(startIndex == text.length() ? (startIndex-1) : startIndex); if(rt == null) { logger.log(POILogger.WARN, "RichTextRun not found at pos" + startIndex + "; text.length: " + text.length()); break; @@ -133,33 +126,58 @@ public class TextPainter { } int endIndex = measurer.getPosition(); + float lineHeight = (float)textLayout.getBounds().getHeight(); + int linespacing = rt.getLineSpacing(); + if(linespacing == 0) linespacing = 100; + TextElement el = new TextElement(); - el.ascent = textLayout.getAscent(); - el._startIndex = startIndex; - el._endIndex = endIndex; + if(linespacing >= 0){ + el.ascent = textLayout.getAscent()*linespacing/100; + } else { + el.ascent = -linespacing*Shape.POINT_DPI/Shape.MASTER_DPI; + } + el._align = rt.getAlignment(); el._text = textLayout; el._textOffset = rt.getTextOffset(); - textHeight += textLayout.getAscent(); - if (prStart || startIndex == 0){ - int spaceBefore = rt.getSpaceBefore(); - if (spaceBefore != 0) { - float val = (float)(textLayout.getAscent() + textLayout.getDescent())* spaceBefore/100; - textHeight += val; - el.ascent += val; + if (prStart){ + int sp = rt.getSpaceBefore(); + float spaceBefore; + if(sp >= 0){ + spaceBefore = lineHeight * sp/100; + } else { + 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)){ it.setIndex(startIndex); AttributedString bat = new AttributedString(Character.toString(rt.getBulletChar()), it.getAttributes()); - int bulletSize = rt.getBulletSize(); - if (bulletSize != -1){ - Float sz = (Float)bat.getIterator().getAttribute(TextAttribute.SIZE); - if(sz != null) bat.addAttribute(TextAttribute.SIZE, new Float(sz.floatValue()*bulletSize/100)); - } + Color clr = rt.getBulletColor(); + if (clr != null) bat.addAttribute(TextAttribute.FOREGROUND, clr); TextLayout bulletLayout = new TextLayout(bat.getIterator(), graphics.getFontRenderContext()); if(text.substring(startIndex, endIndex).length() > 1){ @@ -167,24 +185,6 @@ public class TextPainter { 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); } @@ -242,9 +242,6 @@ public class TextPainter { public TextLayout _bullet; public int _bulletOffset; public int _align; - public int _startIndex; - public int _endIndex; - public float _spacing; public float ascent, descent; } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java index e425b83f1..2f77ac5ff 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java @@ -639,4 +639,20 @@ public class TextRun public Hyperlink[] getHyperlinks(){ 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; + } + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java index c58979757..2a09f2224 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java @@ -642,13 +642,14 @@ public class RichTextRun { * Returns the bullet color */ public Color getBulletColor() { - int rgb = getCharTextPropVal("bullet.color"); - if (rgb >= 0x8000000) { - int idx = rgb % 0x8000000; - ColorSchemeAtom ca = parentRun.getSheet().getColorScheme(); - if(idx >= 0 && idx <= 7) rgb = ca.getColor(idx); - } + int rgb = getParaTextPropVal("bullet.color"); + if(rgb == -1) return getFontColor(); + 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); return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed()); } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java index 797f97cda..78458f063 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java @@ -437,4 +437,33 @@ public class TestPictures extends TestCase{ assertTrue(pdata instanceof WMF); 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()); + } }