}\r
};\r
\r
+ shapes[ShapeTypes.StraightConnector1] = new ShapeOutline(){\r
+ public java.awt.Shape getOutline(Shape shape){\r
+ return new Line2D.Float(0, 0, 21600, 21600);\r
+ }\r
+ };\r
+\r
+\r
}\r
}\r
import org.apache.poi.ddf.*;\r
import org.apache.poi.util.LittleEndian;\r
import org.apache.poi.util.POILogger;\r
+import org.apache.poi.util.HexDump;\r
\r
import java.awt.geom.*;\r
import java.util.ArrayList;\r
return null;\r
}\r
\r
- Rectangle2D bounds = getAnchor2D();\r
- float right = (float)bounds.getX();\r
- float bottom = (float)bounds.getY();\r
-\r
GeneralPath path = new GeneralPath();\r
int numPoints = verticesProp.getNumberOfElementsInArray();\r
int numSegments = segmentsProp.getNumberOfElementsInArray();\r
short x = LittleEndian.getShort(p, 0);\r
short y = LittleEndian.getShort(p, 2);\r
path.moveTo(\r
- ((float)x*POINT_DPI/MASTER_DPI + right),\r
- ((float)y*POINT_DPI/MASTER_DPI + bottom));\r
+ ((float)x*POINT_DPI/MASTER_DPI),\r
+ ((float)y*POINT_DPI/MASTER_DPI));\r
} else if (Arrays.equals(elem, SEGMENTINFO_CUBICTO) || Arrays.equals(elem, SEGMENTINFO_CUBICTO2)){\r
i++;\r
byte[] p1 = verticesProp.getElement(j++);\r
short x3 = LittleEndian.getShort(p3, 0);\r
short y3 = LittleEndian.getShort(p3, 2);\r
path.curveTo(\r
- ((float)x1*POINT_DPI/MASTER_DPI + right), ((float)y1*POINT_DPI/MASTER_DPI + bottom),\r
- ((float)x2*POINT_DPI/MASTER_DPI + right), ((float)y2*POINT_DPI/MASTER_DPI + bottom),\r
- ((float)x3*POINT_DPI/MASTER_DPI + right), ((float)y3*POINT_DPI/MASTER_DPI + bottom));\r
+ ((float)x1*POINT_DPI/MASTER_DPI), ((float)y1*POINT_DPI/MASTER_DPI),\r
+ ((float)x2*POINT_DPI/MASTER_DPI), ((float)y2*POINT_DPI/MASTER_DPI),\r
+ ((float)x3*POINT_DPI/MASTER_DPI), ((float)y3*POINT_DPI/MASTER_DPI));\r
\r
} else if (Arrays.equals(elem, SEGMENTINFO_LINETO)){\r
i++;\r
short x = LittleEndian.getShort(p, 0);\r
short y = LittleEndian.getShort(p, 2);\r
path.lineTo(\r
- ((float)x*POINT_DPI/MASTER_DPI + right), ((float)y*POINT_DPI/MASTER_DPI + bottom));\r
+ ((float)x*POINT_DPI/MASTER_DPI), ((float)y*POINT_DPI/MASTER_DPI));\r
}\r
} else if (Arrays.equals(pnext, SEGMENTINFO_CLOSE)){\r
path.closePath();\r
}\r
}\r
}\r
-\r
return path;\r
}\r
\r
public java.awt.Shape getOutline(){\r
- return getPath();\r
+ GeneralPath path = getPath();\r
+ Rectangle2D anchor = getAnchor2D();\r
+ Rectangle2D bounds = path.getBounds2D();\r
+ AffineTransform at = new AffineTransform();\r
+ at.translate(anchor.getX(), anchor.getY());\r
+ at.scale(\r
+ anchor.getWidth()/bounds.getWidth(),\r
+ anchor.getHeight()/bounds.getHeight()\r
+ );\r
+ return at.createTransformedShape(path);\r
}\r
}\r
}
return false;
}
+
+ /**
+ * Return placeholder by text type
+ */
+ public TextShape getPlaceholder(int type){
+ Shape[] shape = getShapes();
+ for (int i = 0; i < shape.length; i++) {
+ if(shape[i] instanceof TextShape){
+ TextShape tx = (TextShape)shape[i];
+ TextRun run = tx.getTextRun();
+ if(run != null && run.getRunType() == type){
+ return tx;
+ }
+ }
+ }
+ return null;
+ }
}
import java.awt.image.BufferedImage;
import java.awt.*;
import java.awt.geom.Rectangle2D;
+import java.awt.geom.AffineTransform;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
}
public void draw(Graphics2D graphics){
+ AffineTransform at = graphics.getTransform();
+ ShapePainter.paint(this, graphics);
+
PictureData data = getPictureData();
if (data instanceof Bitmap){
BufferedImage img = null;
} else {
logger.log(POILogger.WARN, "Rendering of metafiles is not yet supported. image.type: " + (data == null ? "NA" : data.getClass().getName()));
}
+ graphics.setTransform(at);
}
}
* @param sh - owning shape
*/
protected void afterInsert(Sheet sh){
- PPDrawing ppdrawing = sh.getPPDrawing();
- EscherContainerRecord dgContainer = (EscherContainerRecord) ppdrawing.getEscherRecords()[0];
-
- EscherDgRecord dg = (EscherDgRecord) Shape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID);
-
- int id = allocateShapeId(dg);
- setShapeId(id);
- }
-
- /**
- * Allocates new shape id for the new drawing group id.
- *
- * @param dg EscherDgRecord of the sheet that owns the shape being created
- *
- * @return a new shape id.
- */
- protected int allocateShapeId(EscherDgRecord dg)
- {
- EscherDggRecord dgg = _sheet.getSlideShow().getDocumentRecord().getPPDrawingGroup().getEscherDggRecord();
- if(dgg == null){
- logger.log(POILogger.ERROR, "EscherDggRecord not found");
- return 0;
- }
-
- dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 );
-
- // Add to existing cluster if space available
- for (int i = 0; i < dgg.getFileIdClusters().length; i++)
- {
- EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i];
- if (c.getDrawingGroupId() == dg.getDrawingGroupId() && c.getNumShapeIdsUsed() != 1024)
- {
- int result = c.getNumShapeIdsUsed() + (1024 * (i+1));
- c.incrementShapeId();
- dg.setNumShapes( dg.getNumShapes() + 1 );
- dg.setLastMSOSPID( result );
- if (result >= dgg.getShapeIdMax())
- dgg.setShapeIdMax( result + 1 );
- return result;
- }
- }
-
- // Create new cluster
- dgg.addCluster( dg.getDrawingGroupId(), 0 );
- dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId();
- dg.setNumShapes( dg.getNumShapes() + 1 );
- int result = (1024 * dgg.getFileIdClusters().length);
- dg.setLastMSOSPID( result );
- if (result >= dgg.getShapeIdMax())
- dgg.setShapeIdMax( result + 1 );
- return result;
}
/**
Sheet sheet = getSheet();
shape.setSheet(sheet);
+ shape.setShapeId(sheet.allocateShapeId());
shape.afterInsert(sheet);
-
- if (shape instanceof TextShape) {
- TextShape tbox = (TextShape) shape;
- EscherTextboxWrapper txWrapper = tbox.getEscherTextboxWrapper();
- if(txWrapper != null) getSheet().getPPDrawing().addTextboxWrapper(txWrapper);
- }
}
/**
}
public void draw(Graphics2D graphics){
- Rectangle2D anchor = getAnchor2D();
- Rectangle2D coords = getCoordinates();
- //transform coordinates
AffineTransform at = graphics.getTransform();
- /*
- if(!anchor.equals(coords)){
- graphics.scale(anchor.getWidth()/coords.getWidth(), anchor.getHeight()/coords.getHeight());
- graphics.translate(
- anchor.getX()*coords.getWidth()/anchor.getWidth() - coords.getX(),
- anchor.getY()*coords.getHeight()/anchor.getHeight() - coords.getY());
- }
- */
Shape[] sh = getShapes();
for (int i = 0; i < sh.length; i++) {
sh[i].draw(graphics);
package org.apache.poi.hslf.model;
-import org.apache.poi.ddf.EscherContainerRecord;
-import org.apache.poi.ddf.EscherDgRecord;
-import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.ddf.EscherSpRecord;
+import org.apache.poi.ddf.*;
import org.apache.poi.hslf.record.*;
import org.apache.poi.hslf.usermodel.SlideShow;
+import org.apache.poi.util.POILogger;
import java.util.ArrayList;
import java.util.Iterator;
spgr.addChildRecord(shape.getSpContainer());
shape.setSheet(this);
+ shape.setShapeId(allocateShapeId());
shape.afterInsert(this);
+ }
- // If it's a TextShape, we need to tell the PPDrawing, as it has to
- // track TextboxWrappers specially
- if (shape instanceof TextShape) {
- TextShape tbox = (TextShape) shape;
- EscherTextboxWrapper txWrapper = tbox.getEscherTextboxWrapper();
- if(txWrapper != null) ppdrawing.addTextboxWrapper(txWrapper);
+ /**
+ * Allocates new shape id for the new drawing group id.
+ *
+ * @return a new shape id.
+ */
+ public int allocateShapeId()
+ {
+ EscherDggRecord dgg = _slideShow.getDocumentRecord().getPPDrawingGroup().getEscherDggRecord();
+ EscherDgRecord dg = _container.getPPDrawing().getEscherDgRecord();
+
+ dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 );
+
+ // Add to existing cluster if space available
+ for (int i = 0; i < dgg.getFileIdClusters().length; i++)
+ {
+ EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i];
+ if (c.getDrawingGroupId() == dg.getDrawingGroupId() && c.getNumShapeIdsUsed() != 1024)
+ {
+ int result = c.getNumShapeIdsUsed() + (1024 * (i+1));
+ c.incrementShapeId();
+ dg.setNumShapes( dg.getNumShapes() + 1 );
+ dg.setLastMSOSPID( result );
+ if (result >= dgg.getShapeIdMax())
+ dgg.setShapeIdMax( result + 1 );
+ return result;
+ }
}
+
+ // Create new cluster
+ dgg.addCluster( dg.getDrawingGroupId(), 0, false );
+ dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId();
+ dg.setNumShapes( dg.getNumShapes() + 1 );
+ int result = (1024 * dgg.getFileIdClusters().length);
+ dg.setLastMSOSPID( result );
+ if (result >= dgg.getShapeIdMax())
+ dgg.setShapeIdMax( result + 1 );
+ return result;
}
/**
return lst.remove(shape.getSpContainer());
}
+ /**
+ * Called by SlideShow ater a new sheet is created
+ */
+ public void onCreate(){
+
+ }
+
/**
* Return the master sheet .
*/
EscherSimpleProperty p2 = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH);
int p2val = p2 == null ? 0 : p2.getPropertyValue();
Color clr = null;
- if (p1 != null && (p2val & 0x8) != 0){
- int rgb = p1.getPropertyValue();
+ if ((p2val & 0x8) != 0 || (p2val & 0x10) != 0){
+ int rgb = p1 == null ? 0 : p1.getPropertyValue();
if (rgb >= 0x8000000) {
int idx = rgb % 0x8000000;
if(getSheet() != null) {
package org.apache.poi.hslf.model;
import java.util.Vector;
+import java.util.Iterator;
import java.awt.*;
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.EscherDggRecord;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherDgRecord;
+import org.apache.poi.ddf.EscherSpRecord;
/**
* This class represents a slide in a PowerPoint Document. It allows
_slideNo = newSlideNumber;
}
+ /**
+ * Called by SlideShow ater a new slide is created.
+ * <p>
+ * For Slide we need to do the following:
+ * <li> set id of the drawing group.
+ * <li> set shapeId for the container descriptor and background
+ * </p>
+ */
+ public void onCreate(){
+ //initialize drawing group id
+ EscherDggRecord dgg = getSlideShow().getDocumentRecord().getPPDrawingGroup().getEscherDggRecord();
+ EscherContainerRecord dgContainer = (EscherContainerRecord)getSheetContainer().getPPDrawing().getEscherRecords()[0];
+ EscherDgRecord dg = (EscherDgRecord) Shape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID);
+ int dgId = dgg.getMaxDrawingGroupId() + 1;
+ dg.setOptions((short)(dgId << 4));
+ dgg.setDrawingsSaved(dgg.getDrawingsSaved() + 1);
+
+ for (Iterator it = dgContainer.getChildContainers().iterator(); it.hasNext(); ) {
+ EscherContainerRecord c = (EscherContainerRecord)it.next();
+ EscherSpRecord spr = null;
+ switch(c.getRecordId()){
+ case EscherContainerRecord.SPGR_CONTAINER:
+ EscherContainerRecord dc = (EscherContainerRecord)c.getChildRecords().get(0);
+ spr = dc.getChildById(EscherSpRecord.RECORD_ID);
+ break;
+ case EscherContainerRecord.SP_CONTAINER:
+ spr = c.getChildById(EscherSpRecord.RECORD_ID);
+ break;
+ }
+ if(spr != null) spr.setShapeId(allocateShapeId());
+ }
+
+ //PPT doen't increment the number of saved shapes for group descriptor and background
+ dg.setNumShapes(1);
+ }
+
/**
* Create a <code>TextBox</code> object that represents the slide's title.
*
} else {
switch (txtype) {
case TextHeaderAtom.CENTRE_BODY_TYPE:
+ case TextHeaderAtom.HALF_BODY_TYPE:
case TextHeaderAtom.QUARTER_BODY_TYPE:
txtype = TextHeaderAtom.BODY_TYPE;
break;
package org.apache.poi.hslf.model;\r
\r
import org.apache.poi.hslf.usermodel.RichTextRun;\r
+import org.apache.poi.hslf.record.TextRulerAtom;\r
import org.apache.poi.util.POILogger;\r
import org.apache.poi.util.POILogFactory;\r
\r
public class TextPainter {\r
protected POILogger logger = POILogFactory.getLogger(this.getClass());\r
\r
+ /**\r
+ * Display unicode square if a bullet char can't be displayed,\r
+ * for example, if Wingdings font is used.\r
+ * TODO: map Wingdngs and Symbol to unicode Arial\r
+ */\r
+ protected static final char DEFAULT_BULLET_CHAR = '\u25a0';\r
+\r
protected TextShape _shape;\r
\r
public TextPainter(TextShape shape){\r
*/\r
public AttributedString getAttributedString(TextRun txrun){\r
String text = txrun.getText();\r
+ //TODO: properly process tabs\r
+ text = text.replace('\t', ' ');\r
+ text = text.replace((char)160, ' ');\r
+\r
AttributedString at = new AttributedString(text);\r
RichTextRun[] rt = txrun.getRichTextRuns();\r
for (int i = 0; i < rt.length; i++) {\r
}\r
\r
float wrappingWidth = (float)anchor.getWidth() - _shape.getMarginLeft() - _shape.getMarginRight();\r
- wrappingWidth -= rt.getTextOffset();\r
+ int bulletOffset = rt.getBulletOffset();\r
+ int textOffset = rt.getTextOffset();\r
+ int indent = rt.getIndentLevel();\r
+\r
+ TextRulerAtom ruler = run.getTextRuler();\r
+ if(ruler != null) {\r
+ int bullet_val = ruler.getBulletOffsets()[indent]*Shape.POINT_DPI/Shape.MASTER_DPI;\r
+ int text_val = ruler.getTextOffsets()[indent]*Shape.POINT_DPI/Shape.MASTER_DPI;\r
+ if(bullet_val > text_val){\r
+ int a = bullet_val;\r
+ bullet_val = text_val;\r
+ text_val = a;\r
+ }\r
+ if(bullet_val != 0 ) bulletOffset = bullet_val;\r
+ if(text_val != 0) textOffset = text_val;\r
+ }\r
+\r
+ wrappingWidth -= textOffset;\r
\r
if (_shape.getWordWrap() == TextShape.WrapNone) {\r
wrappingWidth = _shape.getSheet().getSlideShow().getPageSize().width;\r
}\r
\r
el._align = rt.getAlignment();\r
- el._text = textLayout;\r
- el._textOffset = rt.getTextOffset();\r
+ el.advance = textLayout.getAdvance();\r
+ el._textOffset = textOffset;\r
+ el._text = new AttributedString(it, startIndex, endIndex);\r
\r
if (prStart){\r
int sp = rt.getSpaceBefore();\r
Color clr = rt.getBulletColor();\r
if (clr != null) bat.addAttribute(TextAttribute.FOREGROUND, clr);\r
else bat.addAttribute(TextAttribute.FOREGROUND, it.getAttribute(TextAttribute.FOREGROUND));\r
- bat.addAttribute(TextAttribute.FAMILY, it.getAttribute(TextAttribute.FAMILY));\r
- bat.addAttribute(TextAttribute.SIZE, it.getAttribute(TextAttribute.SIZE));\r
\r
- TextLayout bulletLayout = new TextLayout(bat.getIterator(), graphics.getFontRenderContext());\r
+ int fontIdx = rt.getBulletFont();\r
+ if(fontIdx == -1) fontIdx = rt.getFontIndex();\r
+ PPFont bulletFont = _shape.getSheet().getSlideShow().getFont(fontIdx);\r
+ bat.addAttribute(TextAttribute.FAMILY, bulletFont.getFontName());\r
+\r
+ int bulletSize = rt.getBulletSize();\r
+ int fontSize = rt.getFontSize();\r
+ if(bulletSize != -1) fontSize = Math.round(fontSize*bulletSize*0.01f);\r
+ bat.addAttribute(TextAttribute.SIZE, new Float(fontSize));\r
+\r
+ if(!new Font(bulletFont.getFontName(), Font.PLAIN, 1).canDisplay(rt.getBulletChar())){\r
+ bat.addAttribute(TextAttribute.FAMILY, "Arial");\r
+ bat = new AttributedString("" + DEFAULT_BULLET_CHAR, bat.getIterator().getAttributes());\r
+ }\r
+\r
if(text.substring(startIndex, endIndex).length() > 1){\r
- el._bullet = bulletLayout;\r
- el._bulletOffset = rt.getBulletOffset();\r
+ el._bullet = bat;\r
+ el._bulletOffset = bulletOffset;\r
}\r
}\r
lines.add(el);\r
break;\r
case TextShape.AlignCenter:\r
pen.x = anchor.getX() + _shape.getMarginLeft() +\r
- (anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;\r
+ (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;\r
break;\r
case TextShape.AlignRight:\r
pen.x = anchor.getX() + _shape.getMarginLeft() +\r
- (anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight());\r
+ (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight());\r
break;\r
}\r
if(elem._bullet != null){\r
- elem._bullet.draw(graphics, (float)(pen.x + elem._bulletOffset), (float)pen.y);\r
+ graphics.drawString(elem._bullet.getIterator(), (float)(pen.x + elem._bulletOffset), (float)pen.y);\r
+ }\r
+ AttributedCharacterIterator chIt = elem._text.getIterator();\r
+ if(chIt.getEndIndex() > chIt.getBeginIndex()) {\r
+ graphics.drawString(chIt, (float)(pen.x + elem._textOffset), (float)pen.y);\r
}\r
- elem._text.draw(graphics, (float)(pen.x + elem._textOffset), (float)pen.y);\r
-\r
y0 += elem.descent;\r
}\r
}\r
\r
\r
- static class TextElement {\r
- public TextLayout _text;\r
+ public static class TextElement {\r
+ public AttributedString _text;\r
public int _textOffset;\r
- public TextLayout _bullet;\r
+ public AttributedString _bullet;\r
public int _bulletOffset;\r
public int _align;\r
public float ascent, descent;\r
+ public float advance;\r
}\r
}\r
// them to \n
String text = rawText.replace('\r','\n');
- //0xB acts like cariage return in page titles
- text = text.replace((char) 0x0B, '\n');
-
+ int type = _headerAtom == null ? 0 : _headerAtom.getTextType();
+ if(type == TextHeaderAtom.TITLE_TYPE || type == TextHeaderAtom.CENTER_TITLE_TYPE){
+ //0xB acts like cariage return in page titles and like blank in the others
+ text = text.replace((char) 0x0B, '\n');
+ } else {
+ text = text.replace((char) 0x0B, ' ');
+ }
return text;
}
return null;
}
+ public TextRulerAtom getTextRuler(){
+ for (int i = 0; i < _records.length; i++) {
+ if(_records[i] instanceof TextRulerAtom) return (TextRulerAtom)_records[i];
+ }
+ return null;
+
+ }
}
public int getVerticalAlignment(){\r
EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT);\r
- int valign;\r
+ int valign = TextShape.AnchorTop;\r
if (prop == null){\r
+ /**\r
+ * If vertical alignment was not found in the shape properties then try to\r
+ * fetch the master shape and search for the align property there.\r
+ */\r
int type = getTextRun().getRunType();\r
- switch (type){\r
- case TextHeaderAtom.TITLE_TYPE:\r
- case TextHeaderAtom.CENTER_TITLE_TYPE:\r
- valign = TextShape.AnchorMiddle;\r
- break;\r
- default:\r
- valign = TextShape.AnchorTop;\r
- break;\r
+ MasterSheet master = getSheet().getMasterSheet();\r
+ if(master != null){\r
+ TextShape masterShape = master.getPlaceholder(type);\r
+ if(masterShape != null) valign = masterShape.getVerticalAlignment();\r
+ } else {\r
+ //not found in the master sheet. Use the hardcoded defaults.\r
+ switch (type){\r
+ case TextHeaderAtom.TITLE_TYPE:\r
+ case TextHeaderAtom.CENTER_TITLE_TYPE:\r
+ valign = TextShape.AnchorMiddle;\r
+ break;\r
+ default:\r
+ valign = TextShape.AnchorTop;\r
+ break;\r
+ }\r
}\r
} else {\r
valign = prop.getPropertyValue();\r
import org.apache.poi.ddf.*;
import org.apache.poi.hslf.model.ShapeTypes;
+import org.apache.poi.hslf.model.Shape;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Vector;
+import java.util.Iterator;
/**
* These are actually wrappers onto Escher drawings. Make use of
private EscherRecord[] childRecords;
private EscherTextboxWrapper[] textboxWrappers;
+ //cached EscherDgRecord
+ private EscherDgRecord dg;
/**
* Get access to the underlying Escher Records
tw[textboxWrappers.length] = txtbox;
textboxWrappers = tw;
}
+
+ /**
+ * Return EscherDgRecord which keeps track of the number of shapes and shapeId in this drawing group
+ *
+ * @return EscherDgRecord
+ */
+ public EscherDgRecord getEscherDgRecord(){
+ if(dg == null){
+ EscherContainerRecord dgContainer = (EscherContainerRecord)childRecords[0];
+ for(Iterator it = dgContainer.getChildRecords().iterator(); it.hasNext();){
+ EscherRecord r = (EscherRecord) it.next();
+ if(r instanceof EscherDgRecord){
+ dg = (EscherDgRecord)r;
+ break;
+ }
+ }
+ }
+ return dg;
+ }
+
}
public static final Type TxMasterStyleAtom = new Type(4003,TxMasterStyleAtom.class);
public static final Type TxCFStyleAtom = new Type(4004,null);
public static final Type TxPFStyleAtom = new Type(4005,null);
- public static final Type TextRulerAtom = new Type(4006,null);
+ public static final Type TextRulerAtom = new Type(4006,TextRulerAtom.class);
public static final Type TextBookmarkAtom = new Type(4007,null);
public static final Type TextBytesAtom = new Type(4008,TextBytesAtom.class);
public static final Type TxSIStyleAtom = new Type(4009,null);
new ParagraphFlagsTextProp(),
new TextProp(2, 0x80, "bullet.char"),
new TextProp(2, 0x10, "bullet.font"),
+ new TextProp(2, 0x40, "bullet.size"),
new TextProp(4, 0x20, "bullet.color"),
- new TextProp(2, 0x40, "bullet.size"),
new AlignmentTextProp(),
new TextProp(2, 0x100, "text.offset"),
new TextProp(2, 0x200, "para_unknown_2"),
--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.util.zip.InflaterInputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+/**\r
+ * Ruler of a text as it differs from the style's ruler settings.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TextRulerAtom extends RecordAtom {\r
+\r
+ /**\r
+ * Record header.\r
+ */\r
+ private byte[] _header;\r
+\r
+ /**\r
+ * Record data.\r
+ */\r
+ private byte[] _data;\r
+\r
+ //ruler internals\r
+ private int defaultTabSize;\r
+ private int numLevels;\r
+ private int[] tabStops;\r
+ private int[] bulletOffsets = new int[5];\r
+ private int[] textOffsets = new int[5];\r
+\r
+ /**\r
+ * Constructs a new empty ruler atom.\r
+ */\r
+ protected TextRulerAtom() {\r
+ _header = new byte[8];\r
+ _data = new byte[0];\r
+\r
+ LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+ LittleEndian.putInt(_header, 4, _data.length);\r
+ }\r
+\r
+ /**\r
+ * Constructs the ruler atom record from its\r
+ * source data.\r
+ *\r
+ * @param source the source data as a byte array.\r
+ * @param start the start offset into the byte array.\r
+ * @param len the length of the slice in the byte array.\r
+ */\r
+ protected TextRulerAtom(byte[] source, int start, int len) {\r
+ // Get the header.\r
+ _header = new byte[8];\r
+ System.arraycopy(source,start,_header,0,8);\r
+\r
+ // Get the record data.\r
+ _data = new byte[len-8];\r
+ System.arraycopy(source,start+8,_data,0,len-8);\r
+\r
+ try {\r
+ read();\r
+ } catch (Exception e){\r
+ logger.log(POILogger.ERROR, "Failed to parse TextRulerAtom: " + e.getMessage()); \r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Gets the record type.\r
+ *\r
+ * @return the record type.\r
+ */\r
+ public long getRecordType() {\r
+ return RecordTypes.TextRulerAtom.typeID;\r
+ }\r
+\r
+ /**\r
+ * Write the contents of the record back, so it can be written\r
+ * to disk.\r
+ *\r
+ * @param out the output stream to write to.\r
+ * @throws java.io.IOException if an error occurs.\r
+ */\r
+ public void writeOut(OutputStream out) throws IOException {\r
+ out.write(_header);\r
+ out.write(_data);\r
+ }\r
+\r
+ /**\r
+ * Read the record bytes and initialize the internal variables\r
+ */\r
+ private void read(){\r
+ int pos = 0;\r
+ short mask = LittleEndian.getShort(_data); pos += 4;\r
+ short val;\r
+ int[] bits = {1, 0, 2, 3, 8, 4, 9, 5, 10, 6, 11, 7, 12};\r
+ for (int i = 0; i < bits.length; i++) {\r
+ if((mask & 1 << bits[i]) != 0){\r
+ switch (bits[i]){\r
+ case 0:\r
+ //defaultTabSize\r
+ defaultTabSize = LittleEndian.getShort(_data, pos); pos += 2;\r
+ break;\r
+ case 1:\r
+ //numLevels\r
+ numLevels = LittleEndian.getShort(_data, pos); pos += 2;\r
+ break;\r
+ case 2:\r
+ //tabStops\r
+ val = LittleEndian.getShort(_data, pos); pos += 2;\r
+ tabStops = new int[val*2];\r
+ for (int j = 0; j < tabStops.length; j++) {\r
+ tabStops[j] = LittleEndian.getUShort(_data, pos); pos += 2;\r
+ }\r
+ break;\r
+ case 3:\r
+ case 4:\r
+ case 5:\r
+ case 6:\r
+ case 7:\r
+ //bullet.offset\r
+ val = LittleEndian.getShort(_data, pos); pos += 2;\r
+ bulletOffsets[bits[i]-3] = val;\r
+ break;\r
+ case 8:\r
+ case 9:\r
+ case 10:\r
+ case 11:\r
+ case 12:\r
+ //text.offset\r
+ val = LittleEndian.getShort(_data, pos); pos += 2;\r
+ textOffsets[bits[i]-8] = val;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Default distance between tab stops, in master coordinates (576 dpi).\r
+ */\r
+ public int getDefaultTabSize(){\r
+ return defaultTabSize;\r
+ }\r
+\r
+ /**\r
+ * Number of indent levels (maximum 5).\r
+ */\r
+ public int getNumberOfLevels(){\r
+ return numLevels;\r
+ }\r
+\r
+ /**\r
+ * Default distance between tab stops, in master coordinates (576 dpi).\r
+ */\r
+ public int[] getTabStops(){\r
+ return tabStops;\r
+ }\r
+\r
+ /**\r
+ * Paragraph's distance from shape's left margin, in master coordinates (576 dpi).\r
+ */\r
+ public int[] getTextOffsets(){\r
+ return textOffsets;\r
+ }\r
+\r
+ /**\r
+ * First line of paragraph's distance from shape's left margin, in master coordinates (576 dpi).\r
+ */\r
+ public int[] getBulletOffsets(){\r
+ return bulletOffsets;\r
+ }\r
+}\r
import org.apache.poi.hslf.model.textproperties.TextProp;
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
import org.apache.poi.hslf.record.ColorSchemeAtom;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.POILogFactory;
/**
*
*/
public class RichTextRun {
+ protected POILogger logger = POILogFactory.getLogger(this.getClass());
+
/** The TextRun we belong to */
private TextRun parentRun;
/** The SlideShow we belong to */
}
if (prop == null){
Sheet sheet = parentRun.getSheet();
- int txtype = parentRun.getRunType();
- MasterSheet master = sheet.getMasterSheet();
- if (master != null)
- prop = (BitMaskTextProp)master.getStyleAttribute(txtype, getIndentLevel(), propname, isCharacter);
+ if(sheet != null){
+ int txtype = parentRun.getRunType();
+ MasterSheet master = sheet.getMasterSheet();
+ if (master != null){
+ prop = (BitMaskTextProp)master.getStyleAttribute(txtype, getIndentLevel(), propname, isCharacter);
+ }
+ } else {
+ logger.log(POILogger.WARN, "MasterSheet is not available");
+ }
}
return prop == null ? false : prop.getSubValue(index);
* it if required.
*/
private void setCharFlagsTextPropVal(int index, boolean value) {
- setFlag(true, index, value);
+ if(getFlag(true, index) != value) setFlag(true, index, value);
}
public void setFlag(boolean isCharacter, int index, boolean value) {
*/
private int getParaTextPropVal(String propName) {
TextProp prop = null;
+ boolean hardAttribute = false;
if (paragraphStyle != null){
prop = paragraphStyle.findByName(propName);
+
+ BitMaskTextProp maskProp = (BitMaskTextProp)paragraphStyle.findByName(ParagraphFlagsTextProp.NAME);
+ hardAttribute = maskProp != null && maskProp.getValue() == 0;
}
- if (prop == null){
+ if (prop == null && !hardAttribute){
Sheet sheet = parentRun.getSheet();
int txtype = parentRun.getRunType();
MasterSheet master = sheet.getMasterSheet();
return getFlag(false, ParagraphFlagsTextProp.BULLET_IDX);
}
+ /**
+ * Returns whether this rich text run has bullets
+ */
+ public boolean isBulletHard() {
+ return getFlag(false, ParagraphFlagsTextProp.BULLET_IDX);
+ }
+
/**
* Sets the bullet character
*/
import java.awt.Dimension;
import java.io.*;
-import org.apache.poi.ddf.EscherBSERecord;
-import org.apache.poi.ddf.EscherContainerRecord;
-import org.apache.poi.ddf.EscherOptRecord;
-import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.*;
import org.apache.poi.hslf.*;
import org.apache.poi.hslf.model.*;
import org.apache.poi.hslf.model.Notes;
// Lookup between the PersitPtr "sheet" IDs, and the position
// in the mostRecentCoreRecords array
private Hashtable _sheetIdToCoreRecordsLookup;
- // Used when adding new core records
- private int _highestSheetId;
-
+
// Records that are interesting
private Document _documentRecord;
for(int i=0; i<allIDs.length; i++) {
_sheetIdToCoreRecordsLookup.put(new Integer(allIDs[i]), new Integer(i));
}
- // Capture the ID of the highest sheet
- _highestSheetId = allIDs[(allIDs.length-1)];
// Now convert the byte offsets back into record offsets
for(int i=0; i<_records.length; i++) {
}
}
}
-
- // Set up a new SlidePersistAtom for this slide
+
+ // Set up a new SlidePersistAtom for this slide
SlidePersistAtom sp = new SlidePersistAtom();
- // Reference is the 1-based index of the slide container in
- // the PersistPtr root.
- // It always starts with 3 (1 is Document, 2 is MainMaster, 3 is
- // the first slide), but quicksaves etc can leave gaps
- _highestSheetId++;
- sp.setRefID(_highestSheetId);
// First slideId is always 256
sp.setSlideIdentifier(prev == null ? 256 : (prev.getSlideIdentifier() + 1));
-
+
// Add this new SlidePersistAtom to the SlideListWithText
slist.addSlidePersistAtom(sp);
-
-
+
+
// Create a new Slide
Slide slide = new Slide(sp.getSlideIdentifier(), sp.getRefID(), _slides.length+1);
+ slide.setSlideShow(this);
+ slide.onCreate();
+
// Add in to the list of Slides
Slide[] s = new Slide[_slides.length+1];
System.arraycopy(_slides, 0, s, 0, _slides.length);
s[_slides.length] = slide;
_slides = s;
logger.log(POILogger.INFO, "Added slide " + _slides.length + " with ref " + sp.getRefID() + " and identifier " + sp.getSlideIdentifier());
-
+
// Add the core records for this new Slide to the record tree
org.apache.poi.hslf.record.Slide slideRecord = slide.getSlideRecord();
- slideRecord.setSheetId(sp.getRefID());
int slideRecordPos = _hslfSlideShow.appendRootLevelRecord(slideRecord);
_records = _hslfSlideShow.getRecords();
+
// Add the new Slide into the PersistPtr stuff
int offset = 0;
int slideOffset = 0;
Record record = _records[i];
ByteArrayOutputStream out = new ByteArrayOutputStream();
record.writeOut(out);
-
+
// Grab interesting records as they come past
if(_records[i].getRecordType() == RecordTypes.PersistPtrIncrementalBlock.typeID){
ptr = (PersistPtrHolder)_records[i];
if(_records[i].getRecordType() == RecordTypes.UserEditAtom.typeID) {
usr = (UserEditAtom)_records[i];
}
-
+
if(i == slideRecordPos) {
slideOffset = offset;
}
offset += out.size();
}
-
+
+ // persist ID is UserEditAtom.maxPersistWritten + 1
+ int psrId = usr.getMaxPersistWritten() + 1;
+ sp.setRefID(psrId);
+ slideRecord.setSheetId(psrId);
+
+ // Last view is now of the slide
+ usr.setLastViewType((short)UserEditAtom.LAST_VIEW_SLIDE_VIEW);
+ usr.setMaxPersistWritten(psrId); //increment the number of persit objects
+
// Add the new slide into the last PersistPtr
// (Also need to tell it where it is)
slideRecord.setLastOnDiskOffset(slideOffset);
ptr.addSlideLookup(sp.getRefID(), slideOffset);
logger.log(POILogger.INFO, "New slide ended up at " + slideOffset);
- // Last view is now of the slide
- usr.setLastViewType((short)UserEditAtom.LAST_VIEW_SLIDE_VIEW);
- usr.setMaxPersistWritten(_highestSheetId);
-
// All done and added
- slide.setSlideShow(this);
return slide;
}
Freeform p = new Freeform();\r
p.setPath(path1);\r
\r
- GeneralPath path2 = p.getPath();\r
+ java.awt.Shape path2 = p.getOutline();\r
assertTrue(new Area(path1).equals(new Area(path2)));\r
}\r
\r
Freeform p = new Freeform();\r
p.setPath(path1);\r
\r
- GeneralPath path2 = p.getPath();\r
+ java.awt.Shape path2 = p.getOutline();\r
assertTrue(new Area(path1).equals(new Area(path2)));\r
}\r
\r
Freeform p = new Freeform();\r
p.setPath(path1);\r
\r
- GeneralPath path2 = p.getPath();\r
+ java.awt.Shape path2 = p.getOutline();\r
assertTrue(new Area(path1).equals(new Area(path2)));\r
- }\r
+ }\r
}\r
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hslf.usermodel.RichTextRun;
import org.apache.poi.hslf.HSLFSlideShow;
+import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.ddf.EscherDgRecord;
import java.awt.*;
import java.awt.Rectangle;
public void testShapeId() throws IOException {
SlideShow ppt = new SlideShow();
Slide slide = ppt.createSlide();
- Shape shape;
-
- shape = new Line();
- assertEquals(0, shape.getShapeId());
- slide.addShape(shape);
- assertTrue(shape.getShapeId() > 0);
+ Shape shape = null;
+
+ //EscherDgg is a document-level record which keeps track of the drawing groups
+ EscherDggRecord dgg = ppt.getDocumentRecord().getPPDrawingGroup().getEscherDggRecord();
+ EscherDgRecord dg = slide.getSheetContainer().getPPDrawing().getEscherDgRecord();
+
+ int dggShapesUsed = dgg.getNumShapesSaved(); //total number of shapes in the ppt
+ int dggMaxId = dgg.getShapeIdMax(); //max number of shapeId
+
+ int dgMaxId = dg.getLastMSOSPID(); //max shapeId in the slide
+ int dgShapesUsed = dg.getNumShapes(); // number of shapes in the slide
+ //insert 3 shapes and make sure the Ids are properly incremented
+ for (int i = 0; i < 3; i++) {
+ shape = new Line();
+ assertEquals(0, shape.getShapeId());
+ slide.addShape(shape);
+ assertTrue(shape.getShapeId() > 0);
+
+ //check that EscherDgRecord is updated
+ assertEquals(shape.getShapeId(), dg.getLastMSOSPID());
+ assertEquals(dgMaxId + 1, dg.getLastMSOSPID());
+ assertEquals(dgShapesUsed + 1, dg.getNumShapes());
+
+ //check that EscherDggRecord is updated
+ assertEquals(shape.getShapeId() + 1, dgg.getShapeIdMax());
+ assertEquals(dggMaxId + 1, dgg.getShapeIdMax());
+ assertEquals(dggShapesUsed + 1, dgg.getNumShapesSaved());
+
+ dggShapesUsed = dgg.getNumShapesSaved();
+ dggMaxId = dgg.getShapeIdMax();
+ dgMaxId = dg.getLastMSOSPID();
+ dgShapesUsed = dg.getNumShapes();
+ }
- int shapeId = shape.getShapeId();
- shape = new Line();
- assertEquals(0, shape.getShapeId());
- slide.addShape(shape);
- assertEquals(shapeId + 1, shape.getShapeId());
+ //For each drawing group PPT allocates clusters with size=1024
+ //if the number of shapes is greater that 1024 a new cluster is allocated
+ //make sure it is so
+ int numClusters = dgg.getNumIdClusters();
+ for (int i = 0; i < 1025; i++) {
+ shape = new Line();
+ slide.addShape(shape);
+ }
+ assertEquals(numClusters + 1, dgg.getNumIdClusters());
}
}
--- /dev/null
+\r
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+ \r
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import org.apache.poi.hslf.HSLFSlideShow;\r
+import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;\r
+import org.apache.poi.hslf.model.textproperties.TextProp;\r
+import org.apache.poi.hslf.model.textproperties.TextPropCollection;\r
+import org.apache.poi.hslf.record.StyleTextPropAtom.*;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.util.HexDump;\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.LinkedList;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests TextRulerAtom\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestTextRulerAtom extends TestCase {\r
+\r
+ //from a real file\r
+ private byte[] data_1 = new byte[] {\r
+ 0x00, 0x00, (byte)0xA6, 0x0F, 0x18, 0x00, 0x00, 0x00,\r
+ (byte)0xF8, 0x1F, 0x00, 0x00, 0x75, 0x00, (byte)0xE2, 0x00, 0x59,\r
+ 0x01, (byte)0xC3, 0x01, 0x1A, 0x03, (byte)0x87, 0x03, (byte)0xF8,\r
+ 0x03, 0x69, 0x04, (byte)0xF6, 0x05, (byte)0xF6, 0x05\r
+ };\r
+\r
+\r
+ public void testReadRuler() throws Exception {\r
+ TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length);\r
+ assertEquals(ruler.getNumberOfLevels(), 0);\r
+ assertEquals(ruler.getDefaultTabSize(), 0);\r
+\r
+ int[] tabStops = ruler.getTabStops();\r
+ assertNull(tabStops);\r
+\r
+ int[] textOffsets = ruler.getTextOffsets();\r
+ assertTrue(Arrays.equals(new int[]{226, 451, 903, 1129, 1526}, textOffsets));\r
+\r
+ int[] bulletOffsets = ruler.getBulletOffsets();\r
+ assertTrue(Arrays.equals(new int[]{117, 345, 794, 1016, 1526}, bulletOffsets));\r
+\r
+ }\r
+\r
+ public void testWriteRuler() throws Exception {\r
+ TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length);\r
+ ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+ ruler.writeOut(out);\r
+\r
+ byte[] result = out.toByteArray();\r
+ assertTrue(Arrays.equals(result, data_1));\r
+ }\r
+}\r
// Now set it to not bold
rtr.setBold(false);
- assertNotNull(rtr._getRawCharacterStyle());
- assertNotNull(rtr._getRawParagraphStyle());
- assertFalse(rtr.isBold());
+ //setting bold=false doesn't change the internal state
+ assertNull(rtr._getRawCharacterStyle());
+ assertNull(rtr._getRawParagraphStyle());
+
+ assertFalse(rtr.isBold());
// And now make it bold
rtr.setBold(true);