git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@649143 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_0_3_BETA1
@@ -44,6 +44,7 @@ | |||
<li><link href="#OLE">How to retrieve embedded OLE objects</link></li> | |||
<li><link href="#Freeform">How to create shapes of arbitrary geometry</link></li> | |||
<li><link href="#Graphics2D">Shapes and Graphics2D</link></li> | |||
<li><link href="#Render">How to convert slides into images</link></li> | |||
</ul> | |||
</section> | |||
<section><title>Features</title> | |||
@@ -549,7 +550,54 @@ | |||
</source> | |||
</section> | |||
<anchor id="Render"/> | |||
<section><title>Export PowerPoint slides into java.awt.Graphics2D</title> | |||
<p> | |||
HSLF provides a way to export slides into images. You can capture slides into java.awt.Graphics2D object (or any other) | |||
and serialize it into a PNG or JPEG format. Please note, although HSLF attempts to render slides as close to PowerPoint as possible, | |||
the output might look differently from PowerPoint due to the following reasons: | |||
</p> | |||
<ul> | |||
<li>Java2D renders fonts differently vs PowerPoint. There are always some differences in the way the font glyphs are painted</li> | |||
<li>HSLF uses java.awt.font.LineBreakMeasurer to break text into lines. PowerPoint may do it in a different way.</li> | |||
<li>If a font from the presentation is not avaiable, then the JDK default font will be used.</li> | |||
</ul> | |||
<p> | |||
Current Limitations: | |||
</p> | |||
<ul> | |||
<li>Some types of shapes are not yet supported (WordArt, complex auto-shapes)</li> | |||
<li>Only Bitmap images (PNG, JPEG, DIB) can be rendered in Java</li> | |||
</ul> | |||
<source> | |||
FileInputStream is = new FileInputStream("slideshow.ppt"); | |||
SlideShow ppt = new SlideShow(is); | |||
is.close(); | |||
Dimension pgsize = ppt.getPageSize(); | |||
Slide[] slide = ppt.getSlides(); | |||
for (int i = 0; i < slide.length; i++) { | |||
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_RGB); | |||
Graphics2D graphics = img.createGraphics(); | |||
//clear the drawing area | |||
graphics.setPaint(Color.white); | |||
graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height)); | |||
//render | |||
slide[i].draw(graphics); | |||
//save the output | |||
FileOutputStream out = new FileOutputStream("slide-" + (i+1) + ".png"); | |||
javax.imageio.ImageIO.write(img, "png", out); | |||
out.close(); | |||
} | |||
</source> | |||
</section> | |||
</section> | |||
</section> | |||
</body> |
@@ -0,0 +1,103 @@ | |||
/* ==================================================================== | |||
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.hslf.examples; | |||
import org.apache.poi.hslf.usermodel.*; | |||
import org.apache.poi.hslf.model.*; | |||
import org.apache.poi.hslf.record.TextHeaderAtom; | |||
import javax.imageio.ImageIO; | |||
import java.io.IOException; | |||
import java.io.FileOutputStream; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.awt.*; | |||
import java.awt.image.BufferedImage; | |||
import java.awt.geom.AffineTransform; | |||
import java.awt.geom.Rectangle2D; | |||
/** | |||
* Demonstrates how you can use HSLF to convert each slide into a PNG image | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class PPT2PNG { | |||
public static void main(String args[]) throws Exception { | |||
if (args.length == 0) { | |||
usage(); | |||
return; | |||
} | |||
int slidenum = -1; | |||
float scale = 1; | |||
String file = null; | |||
for (int i = 0; i < args.length; i++) { | |||
if (args[i].startsWith("-")) { | |||
if ("-scale".equals(args[i])){ | |||
scale = Float.parseFloat(args[++i]); | |||
} else if ("-slide".equals(args[i])) { | |||
slidenum = Integer.parseInt(args[++i]); | |||
} | |||
} else { | |||
file = args[i]; | |||
} | |||
} | |||
if(file == null){ | |||
usage(); | |||
return; | |||
} | |||
FileInputStream is = new FileInputStream(file); | |||
SlideShow ppt = new SlideShow(is); | |||
is.close(); | |||
Dimension pgsize = ppt.getPageSize(); | |||
int width = (int)(pgsize.width*scale); | |||
int height = (int)(pgsize.height*scale); | |||
Slide[] slide = ppt.getSlides(); | |||
for (int i = 0; i < slide.length; i++) { | |||
if (slidenum != -1 && slidenum != (i+1)) continue; | |||
String title = slide[i].getTitle(); | |||
System.out.println("Rendering slide "+slide[i].getSlideNumber() + (title == null ? "" : ": " + title)); | |||
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); | |||
Graphics2D graphics = img.createGraphics(); | |||
graphics.setPaint(Color.white); | |||
graphics.fill(new Rectangle2D.Float(0, 0, width, height)); | |||
graphics.scale((double)width/pgsize.width, (double)height/pgsize.height); | |||
slide[i].draw(graphics); | |||
String fname = file.replaceAll("\\.ppt", "-" + (i+1) + ".png"); | |||
FileOutputStream out = new FileOutputStream(fname); | |||
ImageIO.write(img, "png", out); | |||
out.close(); | |||
} | |||
} | |||
private static void usage(){ | |||
System.out.println("Usage: PPT2PNG [-scale <scale> -slide <num>] ppt"); | |||
} | |||
} |
@@ -18,6 +18,9 @@ | |||
package org.apache.poi.hslf.model; | |||
import org.apache.poi.ddf.*; | |||
import org.apache.poi.util.POILogger; | |||
import java.awt.geom.Rectangle2D; | |||
/** | |||
* Represents an AutoShape. | |||
@@ -102,4 +105,17 @@ public class AutoShape extends TextShape { | |||
setEscherProperty((short)(EscherProperties.GEOMETRY__ADJUSTVALUE + idx), val); | |||
} | |||
public java.awt.Shape getOutline(){ | |||
ShapeOutline outline = AutoShapes.getShapeOutline(getShapeType()); | |||
Rectangle2D anchor = getAnchor2D(); | |||
if(outline == null){ | |||
logger.log(POILogger.WARN, "getOutline() is not implemented for " + ShapeTypes.typeName(getShapeType())); | |||
return anchor; | |||
} else { | |||
java.awt.Shape shape = outline.getOutline(this); | |||
return AutoShapes.transform(shape, anchor); | |||
} | |||
} | |||
} |
@@ -0,0 +1,293 @@ | |||
/* ==================================================================== | |||
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.hslf.model; | |||
import org.apache.poi.ddf.EscherProperties; | |||
import java.awt.geom.*; | |||
/** | |||
* Stores definition of auto-shapes. | |||
* See the Office Drawing 97-2007 Binary Format Specification for details. | |||
* | |||
* TODO: follow the spec and define all the auto-shapes | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class AutoShapes { | |||
protected static ShapeOutline[] shapes; | |||
/** | |||
* Return shape outline by shape type | |||
* @param type shape type see {@link ShapeTypes} | |||
* | |||
* @return the shape outline | |||
*/ | |||
public static ShapeOutline getShapeOutline(int type){ | |||
ShapeOutline outline = shapes[type]; | |||
return outline; | |||
} | |||
/** | |||
* Auto-shapes are defined in the [0,21600] coordinate system. | |||
* We need to transform it into normal slide coordinates | |||
* | |||
*/ | |||
public static java.awt.Shape transform(java.awt.Shape outline, Rectangle2D anchor){ | |||
AffineTransform at = new AffineTransform(); | |||
at.translate(anchor.getX(), anchor.getY()); | |||
at.scale( | |||
1.0f/21600*anchor.getWidth(), | |||
1.0f/21600*anchor.getHeight() | |||
); | |||
return at.createTransformedShape(outline); | |||
} | |||
static { | |||
shapes = new ShapeOutline[255]; | |||
shapes[ShapeTypes.Rectangle] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
Rectangle2D path = new Rectangle2D.Float(0, 0, 21600, 21600); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.RoundRectangle] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400); | |||
RoundRectangle2D path = new RoundRectangle2D.Float(0, 0, 21600, 21600, adjval, adjval); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.Ellipse] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
Ellipse2D path = new Ellipse2D.Float(0, 0, 21600, 21600); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.Diamond] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(10800, 0); | |||
path.lineTo(21600, 10800); | |||
path.lineTo(10800, 21600); | |||
path.lineTo(0, 10800); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
//m@0,l,21600r21600 | |||
shapes[ShapeTypes.IsocelesTriangle] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 10800); | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(adjval, 0); | |||
path.lineTo(0, 21600); | |||
path.lineTo(21600, 21600); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.RightTriangle] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(0, 0); | |||
path.lineTo(21600, 21600); | |||
path.lineTo(0, 21600); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.Parallelogram] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400); | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(adjval, 0); | |||
path.lineTo(21600, 0); | |||
path.lineTo(21600 - adjval, 21600); | |||
path.lineTo(0, 21600); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.Trapezoid] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400); | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(0, 0); | |||
path.lineTo(adjval, 21600); | |||
path.lineTo(21600 - adjval, 21600); | |||
path.lineTo(21600, 0); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.Hexagon] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400); | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(adjval, 0); | |||
path.lineTo(21600 - adjval, 0); | |||
path.lineTo(21600, 10800); | |||
path.lineTo(21600 - adjval, 21600); | |||
path.lineTo(adjval, 21600); | |||
path.lineTo(0, 10800); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.Octagon] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 6326); | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(adjval, 0); | |||
path.lineTo(21600 - adjval, 0); | |||
path.lineTo(21600, adjval); | |||
path.lineTo(21600, 21600-adjval); | |||
path.lineTo(21600-adjval, 21600); | |||
path.lineTo(adjval, 21600); | |||
path.lineTo(0, 21600-adjval); | |||
path.lineTo(0, adjval); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.Plus] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400); | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(adjval, 0); | |||
path.lineTo(21600 - adjval, 0); | |||
path.lineTo(21600 - adjval, adjval); | |||
path.lineTo(21600, adjval); | |||
path.lineTo(21600, 21600-adjval); | |||
path.lineTo(21600-adjval, 21600-adjval); | |||
path.lineTo(21600-adjval, 21600); | |||
path.lineTo(adjval, 21600); | |||
path.lineTo(adjval, 21600-adjval); | |||
path.lineTo(0, 21600-adjval); | |||
path.lineTo(0, adjval); | |||
path.lineTo(adjval, adjval); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.Pentagon] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(10800, 0); | |||
path.lineTo(21600, 8259); | |||
path.lineTo(21600 - 4200, 21600); | |||
path.lineTo(4200, 21600); | |||
path.lineTo(0, 8259); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.DownArrow] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
//m0@0 l@1@0 @1,0 @2,0 @2@0,21600@0,10800,21600xe | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 16200); | |||
int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400); | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(0, adjval); | |||
path.lineTo(adjval2, adjval); | |||
path.lineTo(adjval2, 0); | |||
path.lineTo(21600-adjval2, 0); | |||
path.lineTo(21600-adjval2, adjval); | |||
path.lineTo(21600, adjval); | |||
path.lineTo(10800, 21600); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.UpArrow] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
//m0@0 l@1@0 @1,21600@2,21600@2@0,21600@0,10800,xe | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400); | |||
int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400); | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(0, adjval); | |||
path.lineTo(adjval2, adjval); | |||
path.lineTo(adjval2, 21600); | |||
path.lineTo(21600-adjval2, 21600); | |||
path.lineTo(21600-adjval2, adjval); | |||
path.lineTo(21600, adjval); | |||
path.lineTo(10800, 0); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.Arrow] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
//m@0, l@0@1 ,0@1,0@2@0@2@0,21600,21600,10800xe | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 16200); | |||
int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400); | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(adjval, 0); | |||
path.lineTo(adjval, adjval2); | |||
path.lineTo(0, adjval2); | |||
path.lineTo(0, 21600-adjval2); | |||
path.lineTo(adjval, 21600-adjval2); | |||
path.lineTo(adjval, 21600); | |||
path.lineTo(21600, 10800); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
shapes[ShapeTypes.LeftArrow] = new ShapeOutline(){ | |||
public java.awt.Shape getOutline(Shape shape){ | |||
//m@0, l@0@1,21600@1,21600@2@0@2@0,21600,,10800xe | |||
int adjval = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUSTVALUE, 5400); | |||
int adjval2 = shape.getEscherProperty(EscherProperties.GEOMETRY__ADJUST2VALUE, 5400); | |||
GeneralPath path = new GeneralPath(); | |||
path.moveTo(adjval, 0); | |||
path.lineTo(adjval, adjval2); | |||
path.lineTo(21600, adjval2); | |||
path.lineTo(21600, 21600-adjval2); | |||
path.lineTo(adjval, 21600-adjval2); | |||
path.lineTo(adjval, 21600); | |||
path.lineTo(0, 10800); | |||
path.closePath(); | |||
return path; | |||
} | |||
}; | |||
} | |||
} |
@@ -22,12 +22,10 @@ import org.apache.poi.ddf.*; | |||
import org.apache.poi.hslf.record.*; | |||
import org.apache.poi.hslf.usermodel.PictureData; | |||
import org.apache.poi.hslf.usermodel.SlideShow; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.util.POILogFactory; | |||
import java.awt.*; | |||
import java.util.*; | |||
/** | |||
* Represents functionality provided by the 'Fill Effects' dialog in PowerPoint. | |||
@@ -137,13 +135,15 @@ public class Fill { | |||
EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(shape.getSpContainer(), EscherOptRecord.RECORD_ID); | |||
EscherSimpleProperty p1 = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__FILLCOLOR); | |||
EscherSimpleProperty p2 = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); | |||
EscherSimpleProperty p3 = (EscherSimpleProperty)Shape.getEscherProperty(opt, EscherProperties.FILL__FILLOPACITY); | |||
int p2val = p2 == null ? 0 : p2.getPropertyValue(); | |||
int alpha = p3 == null ? 255 : ((p3.getPropertyValue() >> 8) & 0xFF); | |||
Color clr = null; | |||
if (p1 != null && (p2val & 0x10) != 0){ | |||
int rgb = p1.getPropertyValue(); | |||
clr = shape.getColor(rgb); | |||
clr = shape.getColor(rgb, alpha); | |||
} | |||
return clr; | |||
} | |||
@@ -176,7 +176,7 @@ public class Fill { | |||
Color clr = null; | |||
if (p1 != null && (p2val & 0x10) != 0){ | |||
int rgb = p1.getPropertyValue(); | |||
clr = shape.getColor(rgb); | |||
clr = shape.getColor(rgb, 255); | |||
} | |||
return clr; | |||
} |
@@ -19,7 +19,6 @@ package org.apache.poi.hslf.model; | |||
import org.apache.poi.ddf.*; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.util.HexDump; | |||
import java.awt.geom.*; | |||
import java.util.ArrayList; | |||
@@ -237,4 +236,8 @@ public class Freeform extends AutoShape { | |||
return path; | |||
} | |||
public java.awt.Shape getOutline(){ | |||
return getPath(); | |||
} | |||
} |
@@ -19,6 +19,9 @@ package org.apache.poi.hslf.model; | |||
import org.apache.poi.ddf.*; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.geom.Line2D; | |||
/** | |||
* Represents a line in a PowerPoint drawing | |||
* | |||
@@ -126,4 +129,8 @@ public class Line extends SimpleShape { | |||
return _escherContainer; | |||
} | |||
public java.awt.Shape getOutline(){ | |||
Rectangle2D anchor = getAnchor2D(); | |||
return new Line2D.Double(anchor.getX(), anchor.getY(), anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight()); | |||
} | |||
} |
@@ -17,6 +17,8 @@ | |||
package org.apache.poi.hslf.model; | |||
import org.apache.poi.hslf.record.SheetContainer; | |||
import org.apache.poi.hslf.record.Record; | |||
import org.apache.poi.hslf.record.RecordTypes; | |||
import org.apache.poi.hslf.model.textproperties.TextProp; | |||
/** | |||
@@ -37,4 +39,32 @@ public abstract class MasterSheet extends Sheet { | |||
*/ | |||
public abstract TextProp getStyleAttribute(int txtype, int level, String name, boolean isCharacter) ; | |||
/** | |||
* Checks if the shape is a placeholder. | |||
* (placeholders aren't normal shapes, they are visible only in the Edit Master mode) | |||
* | |||
* | |||
* @return true if the shape is a placeholder | |||
*/ | |||
public static boolean isPlaceholder(Shape shape){ | |||
if(!(shape instanceof TextShape)) return false; | |||
TextShape tx = (TextShape)shape; | |||
TextRun run = tx.getTextRun(); | |||
if(run == null) return false; | |||
Record[] records = run._records; | |||
for (int i = 0; i < records.length; i++) { | |||
int type = (int)records[i].getRecordType(); | |||
if (type == RecordTypes.BaseTextPropAtom.typeID || | |||
type == RecordTypes.DateTimeMCAtom.typeID || | |||
type == RecordTypes.GenericDateMCAtom.typeID || | |||
type == RecordTypes.FooterMCAtom.typeID || | |||
type == RecordTypes.SlideNumberMCAtom.typeID | |||
) return true; | |||
} | |||
return false; | |||
} | |||
} |
@@ -17,24 +17,13 @@ | |||
package org.apache.poi.hslf.model; | |||
import org.apache.poi.ddf.*; | |||
import org.apache.poi.hslf.usermodel.PictureData; | |||
import org.apache.poi.hslf.usermodel.SlideShow; | |||
import org.apache.poi.hslf.usermodel.ObjectData; | |||
import org.apache.poi.hslf.record.Document; | |||
import org.apache.poi.hslf.record.ExObjList; | |||
import org.apache.poi.hslf.record.Record; | |||
import org.apache.poi.hslf.record.ExEmbed; | |||
import org.apache.poi.hslf.blip.Bitmap; | |||
import org.apache.poi.util.POILogger; | |||
import javax.imageio.ImageIO; | |||
import java.awt.image.BufferedImage; | |||
import java.awt.*; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.util.List; | |||
import java.util.Arrays; | |||
/** | |||
* A shape representing embedded OLE obejct. |
@@ -27,7 +27,6 @@ import java.awt.image.renderable.RenderableImage; | |||
import java.awt.geom.*; | |||
import java.text.AttributedCharacterIterator; | |||
import java.util.Map; | |||
import java.util.ArrayList; | |||
import org.apache.poi.hslf.usermodel.RichTextRun; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.util.POILogger; |
@@ -26,6 +26,7 @@ import org.apache.poi.util.POILogger; | |||
import javax.imageio.ImageIO; | |||
import java.awt.image.BufferedImage; | |||
import java.awt.*; | |||
import java.awt.geom.Rectangle2D; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.util.List; | |||
@@ -202,4 +203,22 @@ public class Picture extends SimpleShape { | |||
} | |||
} | |||
public void draw(Graphics2D graphics){ | |||
PictureData data = 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; | |||
} | |||
Rectangle anchor = getAnchor(); | |||
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()); | |||
} | |||
} | |||
} |
@@ -17,7 +17,6 @@ | |||
package org.apache.poi.hslf.model; | |||
import org.apache.poi.ddf.*; | |||
import org.apache.poi.hslf.model.ShapeTypes; | |||
import org.apache.poi.hslf.record.ColorSchemeAtom; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.util.POILogFactory; | |||
@@ -297,6 +296,17 @@ public abstract class Shape { | |||
return prop == null ? 0 : prop.getPropertyValue(); | |||
} | |||
/** | |||
* Get the value of a simple escher property for this shape. | |||
* | |||
* @param propId The id of the property. One of the constants defined in EscherOptRecord. | |||
*/ | |||
public int getEscherProperty(short propId, int defaultValue){ | |||
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); | |||
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propId); | |||
return prop == null ? defaultValue : prop.getPropertyValue(); | |||
} | |||
/** | |||
* @return The shape container and it's children that can represent this | |||
* shape. | |||
@@ -333,14 +343,14 @@ public abstract class Shape { | |||
_sheet = sheet; | |||
} | |||
protected Color getColor(int rgb){ | |||
protected Color getColor(int rgb, int alpha){ | |||
if (rgb >= 0x8000000) { | |||
int idx = rgb - 0x8000000; | |||
ColorSchemeAtom ca = getSheet().getColorScheme(); | |||
if(idx >= 0 && idx <= 7) rgb = ca.getColor(idx); | |||
} | |||
Color tmp = new Color(rgb, true); | |||
return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed()); | |||
return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed(), alpha); | |||
} | |||
/** | |||
@@ -364,4 +374,16 @@ public abstract class Shape { | |||
return Hyperlink.find(this); | |||
} | |||
public void draw(Graphics2D graphics){ | |||
logger.log(POILogger.INFO, "Rendering " + getShapeName()); | |||
} | |||
/** | |||
* Return shape outline as a java.awt.Shape object | |||
* | |||
* @return the shape outline | |||
*/ | |||
public java.awt.Shape getOutline(){ | |||
return getAnchor2D(); | |||
} | |||
} |
@@ -24,6 +24,7 @@ import org.apache.poi.hslf.record.EscherTextboxWrapper; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.*; | |||
/** | |||
* Represents a group of shapes. | |||
@@ -222,5 +223,11 @@ public class ShapeGroup extends Shape{ | |||
public Hyperlink getHyperlink(){ | |||
return null; | |||
} | |||
public void draw(Graphics2D graphics){ | |||
Shape[] sh = getShapes(); | |||
for (int i = 0; i < sh.length; i++) { | |||
sh[i].draw(graphics); | |||
} | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
/* ==================================================================== | |||
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.hslf.model; | |||
/** | |||
* Date: Apr 17, 2008 | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public interface ShapeOutline { | |||
java.awt.Shape getOutline(Shape shape); | |||
} |
@@ -0,0 +1,85 @@ | |||
/* ==================================================================== | |||
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.hslf.model; | |||
import java.awt.*; | |||
import java.awt.geom.Rectangle2D; | |||
/** | |||
* Paint a shape into java.awt.Graphics2D | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class ShapePainter { | |||
public static void paint(SimpleShape shape, Graphics2D graphics){ | |||
Rectangle2D anchor = shape.getAnchor2D(); | |||
java.awt.Shape outline = shape.getOutline(); | |||
//flip vertical | |||
if(shape.getFlipVertical()){ | |||
graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight()); | |||
graphics.scale(1, -1); | |||
graphics.translate(-anchor.getX(), -anchor.getY()); | |||
} | |||
//flip horizontal | |||
if(shape.getFlipHorizontal()){ | |||
graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY()); | |||
graphics.scale(-1, 1); | |||
graphics.translate(-anchor.getX() , -anchor.getY()); | |||
} | |||
//rotate transform | |||
double angle = shape.getRotation(); | |||
if(angle != 0){ | |||
double centerX = anchor.getX() + anchor.getWidth()/2; | |||
double centerY = anchor.getY() + anchor.getHeight()/2; | |||
graphics.translate(centerX, centerY); | |||
graphics.rotate(Math.toRadians(angle)); | |||
graphics.translate(-centerX, -centerY); | |||
} | |||
//fill | |||
Color fillColor = shape.getFill().getForegroundColor(); | |||
if (fillColor != null) { | |||
graphics.setPaint(fillColor); | |||
graphics.fill(outline); | |||
} | |||
//border | |||
Color lineColor = shape.getLineColor(); | |||
if (lineColor != null){ | |||
graphics.setPaint(lineColor); | |||
float width = (float)shape.getLineWidth(); | |||
int dashing = shape.getLineDashing(); | |||
//TODO: implement more dashing styles | |||
float[] dashptrn = null; | |||
switch(dashing){ | |||
case Line.PEN_PS_DASH: | |||
dashptrn = new float[]{2, 2}; | |||
break; | |||
} | |||
Stroke stroke = new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dashptrn, 0.0f); | |||
graphics.setStroke(stroke); | |||
graphics.draw(outline); | |||
} | |||
} | |||
} |
@@ -29,6 +29,7 @@ import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Vector; | |||
import java.awt.*; | |||
/** | |||
* This class defines the common format of "Sheets" in a powerpoint | |||
@@ -329,4 +330,7 @@ public abstract class Sheet { | |||
return _background; | |||
} | |||
public void draw(Graphics2D graphics){ | |||
} | |||
} |
@@ -22,6 +22,7 @@ import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.hslf.record.ColorSchemeAtom; | |||
import java.awt.*; | |||
import java.awt.geom.AffineTransform; | |||
/** | |||
* An abstract simple (non-group) shape. | |||
@@ -199,4 +200,41 @@ public class SimpleShape extends Shape { | |||
getFill().setForegroundColor(color); | |||
} | |||
/** | |||
* Whether the shape is horizontally flipped | |||
* | |||
* @return whether the shape is horizontally flipped | |||
*/ | |||
public boolean getFlipHorizontal(){ | |||
EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); | |||
return (spRecord.getFlags()& EscherSpRecord.FLAG_FLIPHORIZ) != 0; | |||
} | |||
/** | |||
* Whether the shape is vertically flipped | |||
* | |||
* @return whether the shape is vertically flipped | |||
*/ | |||
public boolean getFlipVertical(){ | |||
EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); | |||
return (spRecord.getFlags()& EscherSpRecord.FLAG_FLIPVERT) != 0; | |||
} | |||
/** | |||
* Rotation angle in degrees | |||
* | |||
* @return rotation angle in degrees | |||
*/ | |||
public int getRotation(){ | |||
int rot = getEscherProperty(EscherProperties.TRANSFORM__ROTATION); | |||
int angle = (rot >> 16) % 360; | |||
return angle; | |||
} | |||
public void draw(Graphics2D graphics){ | |||
AffineTransform at = graphics.getTransform(); | |||
ShapePainter.paint(this, graphics); | |||
graphics.setTransform(at); | |||
} | |||
} |
@@ -21,17 +21,12 @@ | |||
package org.apache.poi.hslf.model; | |||
import java.util.Vector; | |||
import java.util.List; | |||
import java.util.Iterator; | |||
import java.util.ArrayList; | |||
import java.awt.*; | |||
import org.apache.poi.hslf.record.PPDrawing; | |||
import org.apache.poi.hslf.record.SlideAtom; | |||
import org.apache.poi.hslf.record.TextHeaderAtom; | |||
import org.apache.poi.hslf.record.ColorSchemeAtom; | |||
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; | |||
import org.apache.poi.ddf.EscherContainerRecord; | |||
import org.apache.poi.ddf.EscherRecord; | |||
/** | |||
* This class represents a slide in a PowerPoint Document. It allows | |||
@@ -263,10 +258,85 @@ public class Slide extends Sheet | |||
return sa.getFollowMasterBackground(); | |||
} | |||
public Background getBackground() { | |||
/** | |||
* Sets whether this slide draws master sheet objects | |||
* | |||
* @param flag <code>true</code> if the slide draws master sheet objects, | |||
* <code>false</code> otherwise | |||
*/ | |||
public void setFollowMasterObjects(boolean flag){ | |||
SlideAtom sa = getSlideRecord().getSlideAtom(); | |||
sa.setFollowMasterObjects(flag); | |||
} | |||
/** | |||
* Whether this slide follows master color scheme | |||
* | |||
* @return <code>true</code> if the slide follows master color scheme, | |||
* <code>false</code> otherwise | |||
*/ | |||
public boolean getFollowMasterScheme(){ | |||
SlideAtom sa = getSlideRecord().getSlideAtom(); | |||
return sa.getFollowMasterScheme(); | |||
} | |||
/** | |||
* Sets whether this slide draws master color scheme | |||
* | |||
* @param flag <code>true</code> if the slide draws master color scheme, | |||
* <code>false</code> otherwise | |||
*/ | |||
public void setFollowMasterScheme(boolean flag){ | |||
SlideAtom sa = getSlideRecord().getSlideAtom(); | |||
sa.setFollowMasterScheme(flag); | |||
} | |||
/** | |||
* Whether this slide draws master sheet objects | |||
* | |||
* @return <code>true</code> if the slide draws master sheet objects, | |||
* <code>false</code> otherwise | |||
*/ | |||
public boolean getFollowMasterObjects(){ | |||
SlideAtom sa = getSlideRecord().getSlideAtom(); | |||
return sa.getFollowMasterObjects(); | |||
} | |||
/** | |||
* Background for this slide. | |||
*/ | |||
public Background getBackground() { | |||
if(getFollowMasterBackground()) | |||
return getMasterSheet().getBackground(); | |||
else | |||
return super.getBackground(); | |||
} | |||
/** | |||
* Color scheme for this slide. | |||
*/ | |||
public ColorSchemeAtom getColorScheme() { | |||
if(getFollowMasterScheme()){ | |||
return getMasterSheet().getColorScheme(); | |||
} | |||
return super.getColorScheme(); | |||
} | |||
public void draw(Graphics2D graphics){ | |||
MasterSheet master = getMasterSheet(); | |||
if(getFollowMasterBackground()) master.getBackground().draw(graphics); | |||
if(getFollowMasterObjects()){ | |||
Shape[] sh = master.getShapes(); | |||
for (int i = 0; i < sh.length; i++) { | |||
if(MasterSheet.isPlaceholder(sh[i])) continue; | |||
sh[i].draw(graphics); | |||
} | |||
} | |||
Shape[] sh = getShapes(); | |||
for (int i = 0; i < sh.length; i++) { | |||
sh[i].draw(graphics); | |||
} | |||
} | |||
} |
@@ -21,12 +21,6 @@ import org.apache.poi.hslf.model.textproperties.TextProp; | |||
import org.apache.poi.hslf.model.textproperties.TextPropCollection; | |||
import org.apache.poi.hslf.record.*; | |||
import org.apache.poi.hslf.usermodel.SlideShow; | |||
import org.apache.poi.hslf.record.StyleTextPropAtom.*; | |||
import org.apache.poi.ddf.EscherContainerRecord; | |||
import org.apache.poi.ddf.EscherRecord; | |||
import java.util.List; | |||
import java.util.Iterator; | |||
/** | |||
* SlideMaster determines the graphics, layout, and formatting for all the slides in a given presentation. | |||
@@ -82,17 +76,33 @@ public class SlideMaster extends MasterSheet { | |||
if (prop != null) break; | |||
} | |||
if (prop == null) { | |||
switch (txtype) { | |||
case TextHeaderAtom.CENTRE_BODY_TYPE: | |||
case TextHeaderAtom.HALF_BODY_TYPE: | |||
case TextHeaderAtom.QUARTER_BODY_TYPE: | |||
txtype = TextHeaderAtom.BODY_TYPE; | |||
break; | |||
case TextHeaderAtom.CENTER_TITLE_TYPE: | |||
txtype = TextHeaderAtom.TITLE_TYPE; | |||
break; | |||
default: | |||
return null; | |||
if(isCharacter) { | |||
switch (txtype) { | |||
case TextHeaderAtom.CENTRE_BODY_TYPE: | |||
case TextHeaderAtom.HALF_BODY_TYPE: | |||
case TextHeaderAtom.QUARTER_BODY_TYPE: | |||
txtype = TextHeaderAtom.BODY_TYPE; | |||
break; | |||
case TextHeaderAtom.CENTER_TITLE_TYPE: | |||
txtype = TextHeaderAtom.TITLE_TYPE; | |||
break; | |||
default: | |||
return null; | |||
} | |||
} else { | |||
switch (txtype) { | |||
case TextHeaderAtom.CENTRE_BODY_TYPE: | |||
case TextHeaderAtom.QUARTER_BODY_TYPE: | |||
txtype = TextHeaderAtom.BODY_TYPE; | |||
break; | |||
case TextHeaderAtom.CENTER_TITLE_TYPE: | |||
txtype = TextHeaderAtom.TITLE_TYPE; | |||
break; | |||
default: | |||
return null; | |||
} | |||
return null; | |||
} | |||
prop = getStyleAttribute(txtype, level, name, isCharacter); | |||
} | |||
@@ -119,4 +129,34 @@ public class SlideMaster extends MasterSheet { | |||
} | |||
} | |||
} | |||
/** | |||
* Checks if the shape is a placeholder. | |||
* (placeholders aren't normal shapes, they are visible only in the Edit Master mode) | |||
* | |||
* | |||
* @return true if the shape is a placeholder | |||
*/ | |||
public static boolean isPlaceholder(Shape shape){ | |||
if(!(shape instanceof TextShape)) return false; | |||
TextShape tx = (TextShape)shape; | |||
TextRun run = tx.getTextRun(); | |||
if(run == null) return false; | |||
Record[] records = run._records; | |||
for (int i = 0; i < records.length; i++) { | |||
int type = (int)records[i].getRecordType(); | |||
if (type == RecordTypes.OEPlaceholderAtom.typeID || | |||
type == RecordTypes.SlideNumberMCAtom.typeID || | |||
type == RecordTypes.DateTimeMCAtom.typeID || | |||
type == RecordTypes.GenericDateMCAtom.typeID || | |||
type == RecordTypes.FooterMCAtom.typeID ){ | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
} |
@@ -23,7 +23,6 @@ import org.apache.poi.util.LittleEndian; | |||
import java.util.*; | |||
import java.util.List; | |||
import java.awt.*; | |||
import java.awt.geom.Rectangle2D; | |||
/** | |||
* Represents a table in a PowerPoint presentation |
@@ -18,9 +18,7 @@ | |||
package org.apache.poi.hslf.model; | |||
import org.apache.poi.ddf.*; | |||
import org.apache.poi.hslf.record.EscherTextboxWrapper; | |||
import org.apache.poi.hslf.record.TextHeaderAtom; | |||
import org.apache.poi.hslf.usermodel.RichTextRun; | |||
import java.awt.*; | |||
@@ -19,15 +19,6 @@ | |||
package org.apache.poi.hslf.model; | |||
import org.apache.poi.ddf.*; | |||
import org.apache.poi.hslf.record.*; | |||
import org.apache.poi.hslf.usermodel.RichTextRun; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.util.POILogger; | |||
import java.awt.*; | |||
import java.awt.font.FontRenderContext; | |||
import java.awt.font.TextLayout; | |||
import java.io.IOException; | |||
/** | |||
* Represents a TextFrame shape in PowerPoint. |
@@ -0,0 +1,238 @@ | |||
/* ==================================================================== | |||
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.hslf.model; | |||
import org.apache.poi.hslf.usermodel.RichTextRun; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.util.POILogFactory; | |||
import java.text.AttributedString; | |||
import java.text.AttributedCharacterIterator; | |||
import java.awt.font.TextAttribute; | |||
import java.awt.font.LineBreakMeasurer; | |||
import java.awt.font.TextLayout; | |||
import java.awt.*; | |||
import java.awt.geom.Rectangle2D; | |||
import java.awt.geom.Point2D; | |||
import java.util.ArrayList; | |||
/** | |||
* Paint text into java.awt.Graphics2D | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class TextPainter { | |||
protected POILogger logger = POILogFactory.getLogger(this.getClass()); | |||
protected TextShape _shape; | |||
public TextPainter(TextShape shape){ | |||
_shape = shape; | |||
} | |||
public AttributedString getAttributedString(TextRun txrun){ | |||
String text = txrun.getText(); | |||
AttributedString at = new AttributedString(text); | |||
RichTextRun[] rt = txrun.getRichTextRuns(); | |||
for (int i = 0; i < rt.length; i++) { | |||
int start = rt[i].getStartIndex(); | |||
int end = rt[i].getEndIndex(); | |||
if(start == end) continue; | |||
at.addAttribute(TextAttribute.FAMILY, rt[i].getFontName(), start, end); | |||
at.addAttribute(TextAttribute.SIZE, new Float(rt[i].getFontSize()), start, end); | |||
at.addAttribute(TextAttribute.FOREGROUND, rt[i].getFontColor(), start, end); | |||
if(rt[i].isBold()) at.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, start, end); | |||
if(rt[i].isItalic()) at.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, start, end); | |||
if(rt[i].isUnderlined()) { | |||
at.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, start, end); | |||
at.addAttribute(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL, start, end); | |||
} | |||
if(rt[i].isStrikethrough()) at.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, start, end); | |||
int superScript = rt[i].getSuperscript(); | |||
if(superScript != 0) at.addAttribute(TextAttribute.SUPERSCRIPT, superScript > 0 ? TextAttribute.SUPERSCRIPT_SUPER : TextAttribute.SUPERSCRIPT_SUB, start, end); | |||
} | |||
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; | |||
String text = run.getText(); | |||
if (text == null || text.equals("")) return; | |||
AttributedString at = getAttributedString(run); | |||
AttributedCharacterIterator it = at.getIterator(); | |||
int paragraphStart = it.getBeginIndex(); | |||
int paragraphEnd = it.getEndIndex(); | |||
Rectangle2D anchor = _shape.getAnchor2D(); | |||
float textHeight = 0; | |||
ArrayList lines = new ArrayList(); | |||
LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext()); | |||
measurer.setPosition(paragraphStart); | |||
while (measurer.getPosition() < paragraphEnd) { | |||
int startIndex = measurer.getPosition(); | |||
int nextBreak = text.indexOf('\n', measurer.getPosition() + 1); | |||
RichTextRun rt = getRichTextRunAt(startIndex + 1); | |||
if(rt == null) { | |||
logger.log(POILogger.WARN, "RichTextRun not found at pos" + (startIndex + 1) + "; text.length: " + text.length()); | |||
break; | |||
} | |||
float wrappingWidth = (float)anchor.getWidth() - _shape.getMarginLeft() - _shape.getMarginRight(); | |||
wrappingWidth -= rt.getTextOffset(); | |||
if (_shape.getWordWrap() == TextShape.WrapNone) { | |||
wrappingWidth = _shape.getSheet().getSlideShow().getPageSize().width; | |||
} | |||
TextLayout textLayout = measurer.nextLayout(wrappingWidth + 1, | |||
nextBreak == -1 ? paragraphEnd : nextBreak, true); | |||
if (textLayout == null) { | |||
textLayout = measurer.nextLayout(wrappingWidth, | |||
nextBreak == -1 ? paragraphEnd : nextBreak, false); | |||
} | |||
if(textLayout == null){ | |||
logger.log(POILogger.WARN, "Failed to break text into lines: wrappingWidth: "+wrappingWidth+ | |||
"; text: " + rt.getText()); | |||
measurer.setPosition(rt.getEndIndex()); | |||
continue; | |||
} | |||
int endIndex = measurer.getPosition(); | |||
TextElement el = new TextElement(); | |||
el._startIndex = startIndex; | |||
el._endIndex = endIndex; | |||
el._align = rt.getAlignment(); | |||
el._text = textLayout; | |||
el._textOffset = rt.getTextOffset(); | |||
boolean prStart = text.charAt(startIndex) == '\n' || startIndex == 0; | |||
if (text.charAt(startIndex) == '\n'){ | |||
int spaceBefore = rt.getSpaceBefore(); | |||
if (spaceBefore != 0) { | |||
float val = (textLayout.getAscent() + textLayout.getDescent()) * spaceBefore/100; | |||
textHeight += val; | |||
} | |||
} | |||
if(rt.isBullet() && prStart){ | |||
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)); | |||
} | |||
TextLayout bulletLayout = new TextLayout(bat.getIterator(), graphics.getFontRenderContext()); | |||
if(text.substring(startIndex, endIndex).length() > 1){ | |||
el._bullet = bulletLayout; | |||
el._bulletOffset = rt.getBulletOffset(); | |||
} | |||
} | |||
textHeight += textLayout.getAscent() + textLayout.getLeading(); | |||
int lineSpacing = rt.getLineSpacing(); | |||
if(lineSpacing != 0) el._spacing = textLayout.getDescent()*lineSpacing/100; | |||
else el._spacing = textLayout.getDescent(); | |||
textHeight += el._spacing; | |||
lines.add(el); | |||
} | |||
int valign = _shape.getVerticalAlignment(); | |||
double y0 = anchor.getY(); | |||
switch (valign){ | |||
case TextBox.AnchorTopBaseline: | |||
case TextBox.AnchorTop: | |||
y0 += _shape.getMarginTop(); | |||
break; | |||
case TextBox.AnchorBottom: | |||
y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom(); | |||
break; | |||
default: | |||
case TextBox.AnchorMiddle: | |||
float delta = (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom(); | |||
y0 += _shape.getMarginTop() + delta/2; | |||
break; | |||
} | |||
//finally draw the text fragments | |||
for (int i = 0; i < lines.size(); i++) { | |||
TextElement elem = (TextElement)lines.get(i); | |||
y0 += elem._text.getAscent(); | |||
Point2D.Double pen = new Point2D.Double(); | |||
pen.y = y0; | |||
switch (elem._align) { | |||
case TextShape.AlignLeft: | |||
pen.x = anchor.getX() + _shape.getMarginLeft(); | |||
break; | |||
case TextShape.AlignCenter: | |||
pen.x = anchor.getX() + _shape.getMarginLeft() + | |||
(anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight()) / 2; | |||
break; | |||
case TextShape.AlignRight: | |||
pen.x = anchor.getX() + _shape.getMarginLeft() + | |||
(anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight()); | |||
break; | |||
default: | |||
pen.x = anchor.getX() + _shape.getMarginLeft(); | |||
break; | |||
} | |||
if(elem._bullet != null){ | |||
elem._bullet.draw(graphics, (float)(pen.x + elem._bulletOffset), (float)pen.y); | |||
} | |||
elem._text.draw(graphics, (float)(pen.x + elem._textOffset), (float)pen.y); | |||
y0 += elem._text.getDescent(); | |||
y0 += elem._text.getLeading(); | |||
y0 += elem._spacing; | |||
} | |||
} | |||
public static class TextElement { | |||
public TextLayout _text; | |||
public int _textOffset; | |||
public TextLayout _bullet; | |||
public int _bulletOffset; | |||
public int _align; | |||
public int _startIndex; | |||
public int _endIndex; | |||
public float _spacing; | |||
} | |||
} |
@@ -534,6 +534,10 @@ public class TextRun | |||
// The messes things up on everything but a Mac, so translate | |||
// them to \n | |||
String text = rawText.replace('\r','\n'); | |||
//0xB acts like cariage return in page titles | |||
text = text.replace((char) 0x0B, '\n'); | |||
return text; | |||
} | |||
@@ -513,4 +513,12 @@ public abstract class TextShape extends SimpleShape { | |||
} | |||
} | |||
} | |||
public void draw(Graphics2D graphics){ | |||
AffineTransform at = graphics.getTransform(); | |||
ShapePainter.paint(this, graphics); | |||
new TextPainter(this).paint(graphics); | |||
graphics.setTransform(at); | |||
} | |||
} |
@@ -150,7 +150,7 @@ public class StyleTextPropAtom extends RecordAtom | |||
new TextProp(2, 0x800000, "symbol"), | |||
new TextProp(2, 0x20000, "font.size"), | |||
new TextProp(4, 0x40000, "font.color"), | |||
new TextProp(2, 0x80000, "offset"), | |||
new TextProp(2, 0x80000, "superscript"), | |||
new TextProp(2, 0x100000, "char_unknown_1"), | |||
new TextProp(2, 0x1000000, "char_unknown_3"), | |||
new TextProp(2, 0x2000000, "char_unknown_4"), |
@@ -37,9 +37,6 @@ import org.apache.poi.hslf.record.ColorSchemeAtom; | |||
/** | |||
* Represents a run of text, all with the same style | |||
* | |||
* TODO: finish all the getters and setters to the | |||
* font/character/paragraph properties (currently only | |||
* has some of them) | |||
*/ | |||
public class RichTextRun { | |||
/** The TextRun we belong to */ | |||
@@ -125,7 +122,25 @@ public class RichTextRun { | |||
public int getLength() { | |||
return length; | |||
} | |||
/** | |||
* The beginning index, inclusive. | |||
* | |||
* @return the beginning index, inclusive. | |||
*/ | |||
public int getStartIndex(){ | |||
return startPos; | |||
} | |||
/** | |||
* The ending index, exclusive. | |||
* | |||
* @return the ending index, exclusive. | |||
*/ | |||
public int getEndIndex(){ | |||
return startPos + length; | |||
} | |||
/** | |||
* Fetch the text, in output suitable form | |||
*/ | |||
@@ -313,35 +328,143 @@ public class RichTextRun { | |||
// --------------- Friendly getters / setters on rich text properties ------- | |||
/** | |||
* Is the text bold? | |||
*/ | |||
public boolean isBold() { | |||
return isCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX); | |||
} | |||
/** | |||
* Is the text bold? | |||
*/ | |||
public void setBold(boolean bold) { | |||
setCharFlagsTextPropVal(CharFlagsTextProp.BOLD_IDX, bold); | |||
} | |||
/** | |||
* Is the text italic? | |||
*/ | |||
public boolean isItalic() { | |||
return isCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX); | |||
} | |||
/** | |||
* Is the text italic? | |||
*/ | |||
public void setItalic(boolean italic) { | |||
setCharFlagsTextPropVal(CharFlagsTextProp.ITALIC_IDX, italic); | |||
} | |||
/** | |||
* Is the text underlined? | |||
*/ | |||
public boolean isUnderlined() { | |||
return isCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX); | |||
} | |||
/** | |||
* Is the text underlined? | |||
*/ | |||
public void setUnderlined(boolean underlined) { | |||
setCharFlagsTextPropVal(CharFlagsTextProp.UNDERLINE_IDX, underlined); | |||
} | |||
/** | |||
* Does the text have a shadow? | |||
*/ | |||
public boolean isShadowed() { | |||
return isCharFlagsTextPropVal(CharFlagsTextProp.SHADOW_IDX); | |||
} | |||
/** | |||
* Does the text have a shadow? | |||
*/ | |||
public void setShadowed(boolean flag) { | |||
setCharFlagsTextPropVal(CharFlagsTextProp.SHADOW_IDX, flag); | |||
} | |||
/** | |||
* Is this text embossed? | |||
*/ | |||
public boolean isEmbossed() { | |||
return isCharFlagsTextPropVal(CharFlagsTextProp.RELIEF_IDX); | |||
} | |||
/** | |||
* Is this text embossed? | |||
*/ | |||
public void setEmbossed(boolean flag) { | |||
setCharFlagsTextPropVal(CharFlagsTextProp.RELIEF_IDX, flag); | |||
} | |||
/** | |||
* Gets the strikethrough flag | |||
*/ | |||
public boolean isStrikethrough() { | |||
return isCharFlagsTextPropVal(CharFlagsTextProp.STRIKETHROUGH_IDX); | |||
} | |||
/** | |||
* Sets the strikethrough flag | |||
*/ | |||
public void setStrikethrough(boolean flag) { | |||
setCharFlagsTextPropVal(CharFlagsTextProp.STRIKETHROUGH_IDX, flag); | |||
} | |||
/** | |||
* Gets the subscript/superscript option | |||
* | |||
* @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript | |||
*/ | |||
public int getSuperscript() { | |||
int val = getCharTextPropVal("superscript"); | |||
return val == -1 ? 0 : val; | |||
} | |||
/** | |||
* Sets the subscript/superscript option | |||
* | |||
* @param val the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript | |||
*/ | |||
public void setSuperscript(int val) { | |||
setCharTextPropVal("superscript", val); | |||
} | |||
/** | |||
* Gets the font size | |||
*/ | |||
public int getFontSize() { | |||
return getCharTextPropVal("font.size"); | |||
} | |||
/** | |||
* Sets the font size | |||
*/ | |||
public void setFontSize(int fontSize) { | |||
setCharTextPropVal("font.size", fontSize); | |||
} | |||
/** | |||
* Gets the font index | |||
*/ | |||
public int getFontIndex() { | |||
return getCharTextPropVal("font.index"); | |||
} | |||
/** | |||
* Sets the font index | |||
*/ | |||
public void setFontIndex(int idx) { | |||
setCharTextPropVal("font.index", idx); | |||
} | |||
/** | |||
* Sets the font name to use | |||
*/ | |||
public void setFontName(String fontName) { | |||
if (slideShow == null) { | |||
//we can't set font since slideshow is not assigned yet | |||
@@ -352,6 +475,10 @@ public class RichTextRun { | |||
setCharTextPropVal("font.index", fontIdx); | |||
} | |||
} | |||
/** | |||
* Gets the font name | |||
*/ | |||
public String getFontName() { | |||
if (slideShow == null) { | |||
return _fontname; | |||
@@ -427,7 +554,7 @@ public class RichTextRun { | |||
/** | |||
* Sets indentation level | |||
* | |||
* @param level indentation level. Must be in the range [0, 5] | |||
* @param level indentation level. Must be in the range [0, 4] | |||
*/ | |||
public void setIndentLevel(int level) { | |||
if(paragraphStyle != null ) paragraphStyle.setReservedField((short)level); | |||
@@ -488,6 +615,107 @@ public class RichTextRun { | |||
public int getTextOffset() { | |||
return getParaTextPropVal("text.offset")*Shape.POINT_DPI/Shape.MASTER_DPI; | |||
} | |||
/** | |||
* Sets the bullet size | |||
*/ | |||
public void setBulletSize(int size) { | |||
setParaTextPropVal("bullet.size", size); | |||
} | |||
/** | |||
* Returns the bullet size | |||
*/ | |||
public int getBulletSize() { | |||
return getParaTextPropVal("bullet.size"); | |||
} | |||
/** | |||
* Sets the bullet color | |||
*/ | |||
public void setBulletColor(Color color) { | |||
int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB(); | |||
setParaTextPropVal("bullet.color", rgb); | |||
} | |||
/** | |||
* 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); | |||
} | |||
Color tmp = new Color(rgb, true); | |||
return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed()); | |||
} | |||
/** | |||
* Sets the bullet font | |||
*/ | |||
public void setBulletFont(int idx) { | |||
setParaTextPropVal("bullet.font", idx); | |||
} | |||
/** | |||
* Returns the bullet font | |||
*/ | |||
public int getBulletFont() { | |||
return getParaTextPropVal("bullet.font"); | |||
} | |||
/** | |||
* Sets the line spacing. | |||
* <p> | |||
* If linespacing >= 0, then linespacing is a percentage of normal line height. | |||
* If linespacing < 0, the absolute value of linespacing is the spacing in master coordinates. | |||
* </p> | |||
*/ | |||
public void setLineSpacing(int val) { | |||
setParaTextPropVal("linespacing", val); | |||
} | |||
/** | |||
* Returns the line spacing | |||
* <p> | |||
* If linespacing >= 0, then linespacing is a percentage of normal line height. | |||
* If linespacing < 0, the absolute value of linespacing is the spacing in master coordinates. | |||
* </p> | |||
* | |||
* @return the spacing between lines | |||
*/ | |||
public int getLineSpacing() { | |||
int val = getParaTextPropVal("linespacing"); | |||
return val == -1 ? 0 : val; | |||
} | |||
/** | |||
* Sets spacing before a paragraph. | |||
* <p> | |||
* If spacebefore >= 0, then spacebefore is a percentage of normal line height. | |||
* If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates. | |||
* </p> | |||
*/ | |||
public void setSpaceBefore(int val) { | |||
setParaTextPropVal("spacebefore", val); | |||
} | |||
/** | |||
* Returns spacing before a paragraph | |||
* <p> | |||
* If spacebefore >= 0, then spacebefore is a percentage of normal line height. | |||
* If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates. | |||
* </p> | |||
* | |||
* @return the spacing before a paragraph | |||
*/ | |||
public int getSpaceBefore() { | |||
int val = getParaTextPropVal("spacebefore"); | |||
return val == -1 ? 0 : val; | |||
} | |||
// --------------- Internal HSLF methods, not intended for end-user use! ------- | |||
/** |
@@ -141,7 +141,7 @@ public class TestSlideOrdering extends TestCase { | |||
"ROMANCE: AN ANALYSIS", | |||
"AGENDA", | |||
"You are an important supplier of various items that I need", | |||
(char)0x0B + "Although The Psycho set back my relationship process, recovery is luckily enough under way", | |||
'\n' + "Although The Psycho set back my relationship process, recovery is luckily enough under way", | |||
"Since the time that we seriously go out together, you rank highly among existing relationships", | |||
"Although our personal interests are mostly compatible, the greatest gap exists in Sex and Shopping", | |||
"Your physical characteristics are strong when compared with your competition", |