From 6ac6f01ead8eaac98c955b0e57c1eea63b2eb4dd Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Sun, 19 Mar 2006 17:53:49 +0000 Subject: [PATCH] Add Yegor's new slide functionality (see bug 38954), with a bit of refactoring git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@387008 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/hslf/HSLFSlideShow.java | 26 ++ .../org/apache/poi/hslf/model/Ellipse.java | 60 +++ .../src/org/apache/poi/hslf/model/Line.java | 123 +++++ .../src/org/apache/poi/hslf/model/Notes.java | 3 +- .../apache/poi/hslf/model/PPGraphics2D.java | 434 ++++++++++++++++++ .../org/apache/poi/hslf/model/Rectangle.java | 60 +++ .../src/org/apache/poi/hslf/model/Shape.java | 172 +++++++ .../apache/poi/hslf/model/ShapeFactory.java | 61 +++ .../org/apache/poi/hslf/model/ShapeGroup.java | 151 ++++++ .../org/apache/poi/hslf/model/ShapeTypes.java | 257 +++++++++++ .../src/org/apache/poi/hslf/model/Sheet.java | 57 +++ .../apache/poi/hslf/model/SimpleShape.java | 147 ++++++ .../src/org/apache/poi/hslf/model/Slide.java | 10 +- .../poi/hslf/record/ColorSchemeAtom.java | 21 + .../poi/hslf/record/FontCollection.java | 4 +- .../org/apache/poi/hslf/record/PPDrawing.java | 74 +++ .../poi/hslf/record/PersistPtrHolder.java | 11 + .../poi/hslf/record/RecordContainer.java | 11 +- .../src/org/apache/poi/hslf/record/Slide.java | 25 +- .../org/apache/poi/hslf/record/SlideAtom.java | 23 + .../poi/hslf/record/SlideListWithText.java | 11 + .../poi/hslf/record/SlidePersistAtom.java | 21 + .../apache/poi/hslf/record/UserEditAtom.java | 1 + .../apache/poi/hslf/usermodel/SlideShow.java | 158 ++++--- 24 files changed, 1839 insertions(+), 82 deletions(-) create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/Ellipse.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/Line.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/Shape.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/ShapeTypes.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java diff --git a/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java index f12896a140..ed2de1af24 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java +++ b/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java @@ -262,6 +262,7 @@ public class HSLFSlideShow int oldPos = pdr.getLastOnDiskOffset(); int newPos = baos.size(); pdr.setLastOnDiskOffset(newPos); + //System.out.println(i + " " + oldPos + " " + newPos); oldToNewPositions.put(new Integer(oldPos),new Integer(newPos)); pdr.updateOtherRecordReferences(oldToNewPositions); } @@ -317,6 +318,31 @@ public class HSLFSlideShow * Returns an array of all the records found in the slideshow */ public Record[] getRecords() { return _records; } + + /** + * Adds a new root level record, at the end, but before the last + * PersistPtrIncrementalBlock. + */ + public synchronized int appendRootLevelRecord(Record newRecord) { + int addedAt = -1; + Record[] r = new Record[_records.length+1]; + boolean added = false; + for(int i=(_records.length-1); i>=0; i--) { + if(added) { + // Just copy over + r[i] = _records[i]; + } else { + r[(i+1)] = _records[i]; + if(_records[i] instanceof PersistPtrHolder) { + r[i] = newRecord; + added = true; + addedAt = i; + } + } + } + _records = r; + return addedAt; + } /** * Returns an array of the bytes of the file. Only correct after a diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Ellipse.java b/src/scratchpad/src/org/apache/poi/hslf/model/Ellipse.java new file mode 100644 index 0000000000..9db95012c5 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Ellipse.java @@ -0,0 +1,60 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.*; + +import java.awt.*; + +/** + * Represents a ellipse in a PowerPoint drawing + * + * @author Yegor Kozlov + */ +public class Ellipse extends SimpleShape { + + protected Ellipse(EscherContainerRecord escherRecord, Shape parent){ + super(escherRecord, parent); + } + + public Ellipse(Shape parent){ + super(null, parent); + _escherContainer = create(parent instanceof ShapeGroup); + } + + public Ellipse(){ + this(null); + } + + protected EscherContainerRecord create(boolean isChild){ + EscherContainerRecord spcont = super.create(isChild); + spcont.setOptions((short)15); + + EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID); + short type = (ShapeTypes.Ellipse << 4) + 2; + spRecord.setOptions(type); + + //set default properties for a line + EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID); + + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 4)); + opt.sortProperties(); + + return spcont; + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Line.java b/src/scratchpad/src/org/apache/poi/hslf/model/Line.java new file mode 100644 index 0000000000..ea8e32fa6f --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Line.java @@ -0,0 +1,123 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.*; + +import java.awt.*; + +/** + * Represents a line in a PowerPoint drawing + * + * @author Yegor Kozlov + */ +public class Line extends SimpleShape { + /** + * Solid (continuous) pen + */ + public static final int LineSolid = 1; + /** + * PS_DASH system dash style + */ + public static final int LineDashSys = 2; + /** + * PS_DOT system dash style + */ + public static final int LineDotSys = 3; + /** + * PS_DASHDOT system dash style + */ + public static final int LineDashDotSys = 4; + + /** + * PS_DASHDOTDOT system dash style + */ + public static final int LineDashDotDotSys = 5; + /** + * square dot style + */ + public static final int LineDotGEL = 6; + /** + * dash style + */ + public static final int LineDashGEL = 7; + /** + * long dash style + */ + public static final int LineLongDashGEL = 8; + /** + * dash short dash + */ + public static final int LineDashDotGEL = 9; + /** + * long dash short dash + */ + public static final int LineLongDashDotGEL = 10; + /** + * long dash short dash short dash + */ + public static final int LineLongDashDotDotGEL = 11; + + /** + * Decoration of the end of line, + * reserved in API but not supported. + */ + + /** + * Line ends at end point + */ + public static final int EndCapFlat = 0; + /** + * Rounded ends - the default + */ + public static final int EndCapRound = 1; + /** + * Square protrudes by half line width + */ + public static final int EndCapSquare = 2; + + protected Line(EscherContainerRecord escherRecord, Shape parent){ + super(escherRecord, parent); + } + + public Line(Shape parent){ + super(null, parent); + _escherContainer = create(parent instanceof ShapeGroup); + } + + public Line(){ + this(null); + } + + protected EscherContainerRecord create(boolean isChild){ + EscherContainerRecord spcont = super.create(isChild); + spcont.setOptions((short)15); + + EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID); + short type = (ShapeTypes.Line << 4) + 2; + spRecord.setOptions(type); + + //set default properties for a line + EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID); + + //opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 4)); + opt.sortProperties(); + + return spcont; + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Notes.java b/src/scratchpad/src/org/apache/poi/hslf/model/Notes.java index ca1b467a53..f7fd003859 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Notes.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Notes.java @@ -70,4 +70,5 @@ public class Notes extends Sheet * Returns the sheet number */ public int getSheetNumber() { return _sheetNo; } -} + + protected PPDrawing getPPDrawing() { return _notes.getPPDrawing(); }} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java b/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java new file mode 100644 index 0000000000..f84d968d21 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java @@ -0,0 +1,434 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.image.*; +import java.awt.image.renderable.RenderableImage; +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +import org.apache.poi.ddf.EscherProperties; + +/** + * Translates Graphics2D calls into PowerPoint. + * + * @author Yegor Kozlov + */ +public class PPGraphics2D extends Graphics2D { + //The group to write the graphics calls into. + private ShapeGroup group; + + private AffineTransform transform; + private Stroke stroke; + private Paint paint; + private Font font; + private Color foreground; + private Color background = Color.white; + private Shape clip; + + /** + * Construct an powerpoint Graphics object. + * + * @param group The shape group to write the graphics calls into. + */ + public PPGraphics2D(ShapeGroup group){ + this.group = group; + transform = new AffineTransform(); + } + + /** + * @return the shape group being used for drawing + */ + public ShapeGroup getShapeGroup(){ + return group; + } + + public Font getFont(){ + return font; + } + + public void setFont(Font font){ + this.font = font; + } + + public Color getColor(){ + return foreground; + } + + public void setColor(Color color) { + this.foreground = color; + } + + public Stroke getStroke(){ + return stroke; + } + + public void setStroke(Stroke s){ + this.stroke = s; + } + + public Paint getPaint(){ + return paint; + } + + public void setPaint(Paint paint){ + this.paint = paint; + if (paint instanceof Color) setColor((Color)paint); + } + + public AffineTransform getTransform(){ + return (AffineTransform)transform.clone(); + } + + public void setTransform(AffineTransform trans) { + transform = (AffineTransform)trans.clone(); + } + + public void draw(Shape shape){ + if(clip != null) { + if (!clip.getBounds().contains(transform.createTransformedShape(shape).getBounds())) { + //return; + } + } + + PathIterator it = shape.getPathIterator(transform); + double[] prev = null; + double[] coords = new double[6]; + double[] first = new double[6]; + if(!it.isDone()) it.currentSegment(first); //first point + while(!it.isDone()){ + int type = it.currentSegment(coords); + if (prev != null ){ + Line line = new Line(group); + if (stroke instanceof BasicStroke){ + BasicStroke bs = (BasicStroke)stroke; + line.setLineWidth(bs.getLineWidth()); + } + if(getColor() != null) line.setLineColor(getColor()); + if (type == PathIterator.SEG_LINETO) { + line.setAnchor(new java.awt.Rectangle((int)prev[0], (int)prev[1], (int)(coords[0] - prev[0]), (int)(coords[1] - prev[1]))); + } else if (type == PathIterator.SEG_CLOSE){ + line.setAnchor(new java.awt.Rectangle((int)coords[0], (int)coords[1], (int)(first[0] - coords[0]), (int)(first[1] - coords[1]))); + } + group.addShape(line); + } + prev = new double[]{coords[0], coords[1]}; + it.next(); + } + + } + + public void drawString(String string, float x, float y){ + } + + public void fill(Shape shape){ + if (paint instanceof Color){ + Color color = (Color)paint; + } + + throw new RuntimeException("Not implemented"); + } + + public void translate(int x, int y) { + AffineTransform at = new AffineTransform(); + at.translate(x, y); + transform.concatenate(at); + } + + public void clip(Shape shape) { + this.clip = transform.createTransformedShape(shape); + //update size of the escher group which holds the drawing + group.setAnchor(clip.getBounds()); + } + + public Shape getClip() { + return clip; + } + + public void scale(double sx, double sy) { + AffineTransform at = new AffineTransform(); + at.scale(sx, sy); + transform.concatenate(at); + } + //=============================================== + public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + throw new RuntimeException("Not implemented"); + } + + public void drawString(String str, int x, int y) { + throw new RuntimeException("Not implemented"); + } + + public void fillOval(int x, int y, int width, int height) { + throw new RuntimeException("Not implemented"); + } + + public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { + throw new RuntimeException("Not implemented"); + } + + public void fillArc(int x, int y, int width, int height, + int startAngle, int arcAngle) { + throw new RuntimeException("Not implemented"); + } + + public void setPaintMode() { + throw new RuntimeException("Not implemented"); + } + + public void drawArc(int x, int y, int width, int height, + int startAngle, int arcAngle) { + throw new RuntimeException("Not implemented"); + } + + + public void drawPolyline(int xPoints[], int yPoints[], + int nPoints) { + throw new RuntimeException("Not implemented"); + } + + public Graphics create() { + throw new RuntimeException("Not implemented"); + } + + public void drawOval(int x, int y, int width, int height) { + Ellipse ellipse = new Ellipse(); + ellipse.setAnchor(new java.awt.Rectangle(x-width/2, y-height/2, width, height)); + if (stroke instanceof BasicStroke){ + BasicStroke bs = (BasicStroke)stroke; + ellipse.setLineWidth(bs.getLineWidth()); + } + if(getColor() != null) ellipse.setLineColor(getColor()); + if (paint instanceof Color){ + Color color = (Color)paint; + ellipse.setFillColor(color); + } + + group.addShape(ellipse); + } + + public void setXORMode(Color color1) { + throw new RuntimeException("Not implemented"); + } + + + public boolean drawImage(Image img, int x, int y, + Color bgcolor, + ImageObserver observer) { + throw new RuntimeException("Not implemented"); + } + + public boolean drawImage(Image img, int x, int y, + int width, int height, + Color bgcolor, + ImageObserver observer) { + throw new RuntimeException("Not implemented"); + } + + + public boolean drawImage(Image img, + int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + ImageObserver observer) { + throw new RuntimeException("Not implemented"); + } + + public boolean drawImage(Image img, + int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + Color bgcolor, + ImageObserver observer) { + throw new RuntimeException("Not implemented"); + } + + public boolean drawImage(Image img, int x, int y, + ImageObserver observer) { + throw new RuntimeException("Not implemented"); + } + + public boolean drawImage(Image img, int x, int y, + int width, int height, + ImageObserver observer) { + throw new RuntimeException("Not implemented"); + } + + public void dispose() { + throw new RuntimeException("Not implemented"); + } + + public void drawLine(int x1, int y1, int x2, int y2) { + Line line = new Line(); + line.setAnchor(new java.awt.Rectangle(x1, y1, x2-x1, y2-y1)); + if (stroke instanceof BasicStroke){ + BasicStroke bs = (BasicStroke)stroke; + line.setLineWidth(bs.getLineWidth()); + } + if(getColor() != null) line.setLineColor(getColor()); + group.addShape(line); + } + + public void fillPolygon(int xPoints[], int yPoints[], + int nPoints) { + throw new RuntimeException("Not implemented"); + } + + public FontMetrics getFontMetrics(Font f) { + throw new RuntimeException("Not implemented"); + } + + public void fillRect(int x, int y, int width, int height) { + throw new RuntimeException("Not implemented"); + } + + public void drawPolygon(int xPoints[], int yPoints[], + int nPoints) { + throw new RuntimeException("Not implemented"); + } + + public void clipRect(int x, int y, int width, int height) { + throw new RuntimeException("Not implemented"); + } + + public void setClip(Shape clip) { + throw new RuntimeException("Not implemented"); + } + + public java.awt.Rectangle getClipBounds() { + throw new RuntimeException("Not implemented"); + } + + public void drawString(AttributedCharacterIterator iterator, int x, int y) { + throw new RuntimeException("Not implemented"); + } + + public void clearRect(int x, int y, int width, int height) { + throw new RuntimeException("Not implemented"); + } + + public void copyArea(int x, int y, int width, int height, int dx, int dy) { + throw new RuntimeException("Not implemented"); + } + + public void setClip(int x, int y, int width, int height) { + throw new RuntimeException("Not implemented"); + } + + public void rotate(double d) { + throw new RuntimeException("Not implemented"); + + } + + public void rotate(double d, double d1, double d2) { + throw new RuntimeException("Not implemented"); + } + + public void shear(double d, double d1) { + throw new RuntimeException("Not implemented"); + } + + public FontRenderContext getFontRenderContext() { + return new FontRenderContext(transform, true, true); + } + + public void transform(AffineTransform affinetransform) { + throw new RuntimeException("Not implemented"); + } + + public void drawImage(BufferedImage bufferedimage, BufferedImageOp op, int x, int y) { + throw new RuntimeException("Not implemented"); + } + + public void setBackground(Color c) { + throw new RuntimeException("Not implemented"); + } + + public void drawRenderedImage(RenderedImage renderedimage, AffineTransform affinetransform) { + throw new RuntimeException("Not implemented"); + } + + public Color getBackground() { + throw new RuntimeException("Not implemented"); + } + + public void setComposite(Composite composite) { + throw new RuntimeException("Not implemented"); + + } + + public Composite getComposite() { + throw new RuntimeException("Not implemented"); + } + + public Object getRenderingHint(java.awt.RenderingHints.Key key) { + throw new RuntimeException("Not implemented"); + } + + public boolean drawImage(Image image, AffineTransform affinetransform, ImageObserver imageobserver) { + throw new RuntimeException("Not implemented"); + } + + public void setRenderingHint(java.awt.RenderingHints.Key key, Object obj) { + throw new RuntimeException("Not implemented"); + } + + + public void drawGlyphVector(GlyphVector g, float x, float y) { + throw new RuntimeException("Not implemented"); + + } + + public GraphicsConfiguration getDeviceConfiguration() { + throw new RuntimeException("Not implemented"); + } + + public void addRenderingHints(Map map) { + throw new RuntimeException("Not implemented"); + } + + public void translate(double d, double d1) { + + throw new RuntimeException("Not implemented"); + } + + public void drawString(AttributedCharacterIterator attributedcharacteriterator, float x, float y) { + throw new RuntimeException("Not implemented"); + } + + public boolean hit(java.awt.Rectangle rectangle, Shape shape, boolean flag) { + throw new RuntimeException("Not implemented"); + } + + public RenderingHints getRenderingHints() { + throw new RuntimeException("Not implemented"); + } + + public void setRenderingHints(Map map) { + throw new RuntimeException("Not implemented"); + + } + + public void drawRenderableImage(RenderableImage renderableimage, AffineTransform affinetransform) { + throw new RuntimeException("Not implemented"); + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java b/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java new file mode 100644 index 0000000000..a4be8e2711 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java @@ -0,0 +1,60 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.*; + +import java.awt.*; + +/** + * Represents a line in a PowerPoint drawing + * + * @author Yegor Kozlov + */ +public class Rectangle extends SimpleShape { + + protected Rectangle(EscherContainerRecord escherRecord, Shape parent){ + super(escherRecord, parent); + } + + public Rectangle(Shape parent){ + super(null, parent); + _escherContainer = create(parent instanceof ShapeGroup); + } + + public Rectangle(){ + this(null); + } + + protected EscherContainerRecord create(boolean isChild){ + EscherContainerRecord spcont = super.create(isChild); + spcont.setOptions((short)15); + + EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID); + short type = (ShapeTypes.Rectangle << 4) + 2; + spRecord.setOptions(type); + + //set default properties for a rectangle + EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID); + + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 4)); + opt.sortProperties(); + + return spcont; + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java new file mode 100644 index 0000000000..a01b116917 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java @@ -0,0 +1,172 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.*; +import org.apache.poi.hslf.model.ShapeTypes; + +import java.awt.*; +import java.util.Iterator; + +/** + * Represents a Shape which is the elemental object that composes a drawing. + * + * @author Yegor Kozlov + */ +public class Shape { + + public static final int EMU_PER_POINT = 12700; + + /** + * The parent of the shape + */ + protected Shape _parent; + + /** + * Either EscherSpContainer or EscheSpgrContainer record + * which holds information about this shape. + */ + protected EscherContainerRecord _escherContainer; + + protected Shape(EscherContainerRecord escherRecord, Shape parent){ + _escherContainer = escherRecord; + _parent = parent; + } + + /** + * @return the parent of this shape + */ + public Shape getParent(){ + return _parent; + } + + /** + * @return name of the shape. + */ + public String getShapeName(){ + EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + return ShapeTypes.typeName(spRecord.getOptions() >> 4); + } + + /** + * Returns the anchor (the bounding box rectangle) of this shape. + * All coordinates are expressed in Master units (576 dpi). + * + * @return the anchor of this shape + */ + public java.awt.Rectangle getAnchor(){ + EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + int flags = spRecord.getFlags(); + java.awt.Rectangle anchor=null; + if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ + EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); + anchor = new java.awt.Rectangle(); + anchor.x = rec.getDx1(); + anchor.y = rec.getDy1(); + anchor.width = rec.getDx2() - anchor.x; + anchor.height = rec.getDy2() - anchor.y; + } + else { + EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); + anchor = new java.awt.Rectangle(); + anchor.y = rec.getFlag(); + anchor.x = rec.getCol1(); + anchor.width = rec.getDx1() - anchor.x; + anchor.height = rec.getRow1() - anchor.y; + } + return anchor; + } + + /** + * Sets the anchor (the bounding box rectangle) of this shape. + * All coordinates should be expressed in Master units (576 dpi). + * + * @param anchor new anchor + */ + public void setAnchor(java.awt.Rectangle anchor){ + EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + int flags = spRecord.getFlags(); + if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ + EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); + rec.setDx1(anchor.x); + rec.setDy1(anchor.y); + rec.setDx2(anchor.width + anchor.x); + rec.setDy2(anchor.height + anchor.y); + } + else { + EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); + rec.setFlag((short)anchor.y); + rec.setCol1((short)anchor.x); + rec.setDx1((short)(anchor.width + anchor.x)); + rec.setRow1((short)(anchor.height + anchor.y)); + } + + } + + /** + * Moves the top left corner of the shape to the specified point. + * + * @param x the x coordinate of the top left corner of the shape + * @param y the y coordinate of the top left corner of the shape + */ + public void moveTo(int x, int y){ + java.awt.Rectangle anchor = getAnchor(); + anchor.setLocation(x, y); + setAnchor(anchor); + } + + protected static EscherRecord getEscherChild(EscherContainerRecord owner, int recordId){ + for ( Iterator iterator = owner.getChildRecords().iterator(); iterator.hasNext(); ) + { + EscherRecord escherRecord = (EscherRecord) iterator.next(); + if (escherRecord.getRecordId() == recordId) + return (EscherRecord) escherRecord; + } + return null; + } + + protected static EscherProperty getEscherProperty(EscherOptRecord opt, int propId){ + for ( Iterator iterator = opt.getEscherProperties().iterator(); iterator.hasNext(); ) + { + EscherProperty prop = (EscherProperty) iterator.next(); + if (prop.getId() == propId) + return prop; + } + return null; + } + + protected static void setEscherProperty(EscherOptRecord opt, short propId, int value){ + java.util.List props = opt.getEscherProperties(); + for ( Iterator iterator = props.iterator(); iterator.hasNext(); ) { + EscherProperty prop = (EscherProperty) iterator.next(); + if (prop.getId() == propId){ + iterator.remove(); + } + } + if (value != -1) { + opt.addEscherProperty(new EscherSimpleProperty(propId, value)); + opt.sortProperties(); + } + } + + /** + * + * @return escher container which holds information about this shape + */ + public EscherContainerRecord getShapeRecord(){ + return _escherContainer; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java new file mode 100644 index 0000000000..67f2d708bd --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java @@ -0,0 +1,61 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.EscherSpRecord; +import org.apache.poi.ddf.EscherContainerRecord; + +/** + * Create a Shape object depending on its type + * + * @author Yegor Kozlov + */ +public class ShapeFactory { + + public static Shape createShape(EscherContainerRecord spContainer, Shape parent){ + if (spContainer.getRecordId() == EscherContainerRecord.SPGR_CONTAINER){ + return new ShapeGroup(spContainer, parent); + } + + Shape shape; + EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID); + + int type = spRecord.getOptions() >> 4; + switch (type){ + case ShapeTypes.TextBox: + case ShapeTypes.Rectangle: + shape = new Shape(spContainer, parent); + break; + case ShapeTypes.PictureFrame: + shape = new Shape(spContainer, parent); + break; + case ShapeTypes.Line: + shape = new Line(spContainer, parent); + break; + case ShapeTypes.Ellipse: + shape = new Ellipse(spContainer, parent); + break; + case ShapeTypes.NotPrimitive: + shape = new ShapeGroup(spContainer, parent); + break; + default: + shape = new Shape(spContainer, parent); + break; + } + return shape; + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java new file mode 100644 index 0000000000..c11e294716 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java @@ -0,0 +1,151 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.*; +import org.apache.poi.util.LittleEndian; + +import java.util.List; + +/** + * Represents a group of shapes. + * + * @author Yegor Kozlov + */ +public class ShapeGroup extends Shape{ + + public ShapeGroup(Shape parent){ + super(null, parent); + _escherContainer = create(); + } + + public ShapeGroup(){ + this(null); + } + + protected ShapeGroup(EscherContainerRecord escherRecord, Shape parent){ + super(escherRecord, parent); + } + + /** + * @return the shapes contained in this group container + */ + public Shape[] getShapes() { + //several SpContainers, the first of which is the group shape itself + List lst = _escherContainer.getChildRecords(); + + //don't include the first SpContainer, it is always NotPrimitive + Shape[] shapes = new Shape[lst.size() - 1]; + for (int i = 1; i < lst.size(); i++){ + EscherContainerRecord container = (EscherContainerRecord)lst.get(i); + shapes[i-1] = ShapeFactory.createShape(container, this); + } + return shapes; + } + + /** + * Sets the anchor (the bounding box rectangle) of this shape. + * All coordinates should be expressed in Master units (576 dpi). + * + * @param anchor new anchor + */ + public void setAnchor(java.awt.Rectangle anchor){ + + EscherContainerRecord spContainer = (EscherContainerRecord)_escherContainer.getChildRecords().get(0); + + EscherClientAnchorRecord clientAnchor = (EscherClientAnchorRecord)getEscherChild(spContainer, EscherClientAnchorRecord.RECORD_ID); + //hack. internal variable EscherClientAnchorRecord.shortRecord can be + //initialized only in fillFields(). We need to set shortRecord=false; + byte[] header = new byte[16]; + LittleEndian.putUShort(header, 0, 0); + LittleEndian.putUShort(header, 2, 0); + LittleEndian.putInt(header, 4, 8); + clientAnchor.fillFields(header, 0, null); + + clientAnchor.setFlag((short)anchor.y); + clientAnchor.setCol1((short)anchor.x); + clientAnchor.setDx1((short)(anchor.width + anchor.x)); + clientAnchor.setRow1((short)(anchor.height + anchor.y)); + + EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(spContainer, EscherSpgrRecord.RECORD_ID); + + spgr.setRectX1(anchor.x); + spgr.setRectY1(anchor.y); + spgr.setRectX2(anchor.x + anchor.width); + spgr.setRectY2(anchor.y + anchor.height); + } + + /** + * Create a new ShapeGroup and create an instance of EscherSpgrContainer which represents a group of shapes + */ + protected EscherContainerRecord create() { + EscherContainerRecord spgr = new EscherContainerRecord(); + spgr.setRecordId(EscherContainerRecord.SPGR_CONTAINER); + spgr.setOptions((short)15); + + //The group itself is a shape, and always appears as the first EscherSpContainer in the group container. + EscherContainerRecord spcont = new EscherContainerRecord(); + spcont.setRecordId(EscherContainerRecord.SP_CONTAINER); + spcont.setOptions((short)15); + + EscherSpgrRecord spg = new EscherSpgrRecord(); + spg.setOptions((short)1); + spcont.addChildRecord(spg); + + EscherSpRecord sp = new EscherSpRecord(); + short type = (ShapeTypes.NotPrimitive << 4) + 2; + sp.setOptions(type); + sp.setFlags(EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_GROUP); + spcont.addChildRecord(sp); + + EscherClientAnchorRecord anchor = new EscherClientAnchorRecord(); + spcont.addChildRecord(anchor); + + spgr.addChildRecord(spcont); + return spgr; + } + + /** + * Add a shape to this group. + * + * @param shape - the Shape to add + */ + public void addShape(Shape shape){ + _escherContainer.addChildRecord(shape.getShapeRecord()); + } + + /** + * Moves this ShapeGroup to the specified location. + *

+ * @param x the x coordinate of the top left corner of the shape in new location + * @param y the y coordinate of the top left corner of the shape in new location + */ + public void moveTo(int x, int y){ + java.awt.Rectangle anchor = getAnchor(); + int dx = x - anchor.x; + int dy = y - anchor.y; + anchor.translate(dx, dy); + setAnchor(anchor); + + Shape[] shape = getShapes(); + for (int i = 0; i < shape.length; i++) { + java.awt.Rectangle chanchor = shape[i].getAnchor(); + chanchor.translate(dx, dy); + shape[i].setAnchor(chanchor); + } + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeTypes.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeTypes.java new file mode 100644 index 0000000000..fd571c1818 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeTypes.java @@ -0,0 +1,257 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed 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.util.HashMap; +import java.lang.reflect.Field; + +/** + * Contains all known shape types in PowerPoint + * + * @author Yegor Kozlov + */ +public class ShapeTypes { + public static final int NotPrimitive = 0; + public static final int Rectangle = 1; + public static final int RoundRectangle = 2; + public static final int Ellipse = 3; + public static final int Diamond = 4; + public static final int IsocelesTriangle = 5; + public static final int RightTriangle = 6; + public static final int Parallelogram = 7; + public static final int Trapezoid = 8; + public static final int Hexagon = 9; + public static final int Octagon = 10; + public static final int Plus = 11; + public static final int Star = 12; + public static final int Arrow = 13; + public static final int ThickArrow = 14; + public static final int HomePlate = 15; + public static final int Cube = 16; + public static final int Balloon = 17; + public static final int Seal = 18; + public static final int Arc = 19; + public static final int Line = 20; + public static final int Plaque = 21; + public static final int Can = 22; + public static final int Donut = 23; + public static final int TextSimple = 24; + public static final int TextOctagon = 25; + public static final int TextHexagon = 26; + public static final int TextCurve = 27; + public static final int TextWave = 28; + public static final int TextRing = 29; + public static final int TextOnCurve = 30; + public static final int TextOnRing = 31; + public static final int StraightConnector1 = 32; + public static final int BentConnector2 = 33; + public static final int BentConnector3 = 34; + public static final int BentConnector4 = 35; + public static final int BentConnector5 = 36; + public static final int CurvedConnector2 = 37; + public static final int CurvedConnector3 = 38; + public static final int CurvedConnector4 = 39; + public static final int CurvedConnector5 = 40; + public static final int Callout1 = 41; + public static final int Callout2 = 42; + public static final int Callout3 = 43; + public static final int AccentCallout1 = 44; + public static final int AccentCallout2 = 45; + public static final int AccentCallout3 = 46; + public static final int BorderCallout1 = 47; + public static final int BorderCallout2 = 48; + public static final int BorderCallout3 = 49; + public static final int AccentBorderCallout1 = 50; + public static final int AccentBorderCallout2 = 51; + public static final int AccentBorderCallout3 = 52; + public static final int Ribbon = 53; + public static final int Ribbon2 = 54; + public static final int Chevron = 55; + public static final int Pentagon = 56; + public static final int NoSmoking = 57; + public static final int Seal8 = 58; + public static final int Seal16 = 59; + public static final int Seal32 = 60; + public static final int WedgeRectCallout = 61; + public static final int WedgeRRectCallout = 62; + public static final int WedgeEllipseCallout = 63; + public static final int Wave = 64; + public static final int FoldedCorner = 65; + public static final int LeftArrow = 66; + public static final int DownArrow = 67; + public static final int UpArrow = 68; + public static final int LeftRightArrow = 69; + public static final int UpDownArrow = 70; + public static final int IrregularSeal1 = 71; + public static final int IrregularSeal2 = 72; + public static final int LightningBolt = 73; + public static final int Heart = 74; + public static final int PictureFrame = 75; + public static final int QuadArrow = 76; + public static final int LeftArrowCallout = 77; + public static final int RightArrowCallout = 78; + public static final int UpArrowCallout = 79; + public static final int DownArrowCallout = 80; + public static final int LeftRightArrowCallout = 81; + public static final int UpDownArrowCallout = 82; + public static final int QuadArrowCallout = 83; + public static final int Bevel = 84; + public static final int LeftBracket = 85; + public static final int RightBracket = 86; + public static final int LeftBrace = 87; + public static final int RightBrace = 88; + public static final int LeftUpArrow = 89; + public static final int BentUpArrow = 90; + public static final int BentArrow = 91; + public static final int Seal24 = 92; + public static final int StripedRightArrow = 93; + public static final int NotchedRightArrow = 94; + public static final int BlockArc = 95; + public static final int SmileyFace = 96; + public static final int VerticalScroll = 97; + public static final int HorizontalScroll = 98; + public static final int CircularArrow = 99; + public static final int NotchedCircularArrow = 100; + public static final int UturnArrow = 101; + public static final int CurvedRightArrow = 102; + public static final int CurvedLeftArrow = 103; + public static final int CurvedUpArrow = 104; + public static final int CurvedDownArrow = 105; + public static final int CloudCallout = 106; + public static final int EllipseRibbon = 107; + public static final int EllipseRibbon2 = 108; + public static final int FlowChartProcess = 109; + public static final int FlowChartDecision = 110; + public static final int FlowChartInputOutput = 111; + public static final int FlowChartPredefinedProcess = 112; + public static final int FlowChartInternalStorage = 113; + public static final int FlowChartDocument = 114; + public static final int FlowChartMultidocument = 115; + public static final int FlowChartTerminator = 116; + public static final int FlowChartPreparation = 117; + public static final int FlowChartManualInput = 118; + public static final int FlowChartManualOperation = 119; + public static final int FlowChartConnector = 120; + public static final int FlowChartPunchedCard = 121; + public static final int FlowChartPunchedTape = 122; + public static final int FlowChartSummingJunction = 123; + public static final int FlowChartOr = 124; + public static final int FlowChartCollate = 125; + public static final int FlowChartSort = 126; + public static final int FlowChartExtract = 127; + public static final int FlowChartMerge = 128; + public static final int FlowChartOfflineStorage = 129; + public static final int FlowChartOnlineStorage = 130; + public static final int FlowChartMagneticTape = 131; + public static final int FlowChartMagneticDisk = 132; + public static final int FlowChartMagneticDrum = 133; + public static final int FlowChartDisplay = 134; + public static final int FlowChartDelay = 135; + public static final int TextPlainText = 136; + public static final int TextStop = 137; + public static final int TextTriangle = 138; + public static final int TextTriangleInverted = 139; + public static final int TextChevron = 140; + public static final int TextChevronInverted = 141; + public static final int TextRingInside = 142; + public static final int TextRingOutside = 143; + public static final int TextArchUpCurve = 144; + public static final int TextArchDownCurve = 145; + public static final int TextCircleCurve = 146; + public static final int TextButtonCurve = 147; + public static final int TextArchUpPour = 148; + public static final int TextArchDownPour = 149; + public static final int TextCirclePour = 150; + public static final int TextButtonPour = 151; + public static final int TextCurveUp = 152; + public static final int TextCurveDown = 153; + public static final int TextCascadeUp = 154; + public static final int TextCascadeDown = 155; + public static final int TextWave1 = 156; + public static final int TextWave2 = 157; + public static final int TextWave3 = 158; + public static final int TextWave4 = 159; + public static final int TextInflate = 160; + public static final int TextDeflate = 161; + public static final int TextInflateBottom = 162; + public static final int TextDeflateBottom = 163; + public static final int TextInflateTop = 164; + public static final int TextDeflateTop = 165; + public static final int TextDeflateInflate = 166; + public static final int TextDeflateInflateDeflate = 167; + public static final int TextFadeRight = 168; + public static final int TextFadeLeft = 169; + public static final int TextFadeUp = 170; + public static final int TextFadeDown = 171; + public static final int TextSlantUp = 172; + public static final int TextSlantDown = 173; + public static final int TextCanUp = 174; + public static final int TextCanDown = 175; + public static final int FlowChartAlternateProcess = 176; + public static final int FlowChartOffpageConnector = 177; + public static final int Callout90 = 178; + public static final int AccentCallout90 = 179; + public static final int BorderCallout90 = 180; + public static final int AccentBorderCallout90 = 181; + public static final int LeftRightUpArrow = 182; + public static final int Sun = 183; + public static final int Moon = 184; + public static final int BracketPair = 185; + public static final int BracePair = 186; + public static final int Seal4 = 187; + public static final int DoubleWave = 188; + public static final int ActionButtonBlank = 189; + public static final int ActionButtonHome = 190; + public static final int ActionButtonHelp = 191; + public static final int ActionButtonInformation = 192; + public static final int ActionButtonForwardNext = 193; + public static final int ActionButtonBackPrevious = 194; + public static final int ActionButtonEnd = 195; + public static final int ActionButtonBeginning = 196; + public static final int ActionButtonReturn = 197; + public static final int ActionButtonDocument = 198; + public static final int ActionButtonSound = 199; + public static final int ActionButtonMovie = 200; + public static final int HostControl = 201; + public static final int TextBox = 202; + + /** + * Return name of the shape by id + * @param type - the id of the shape, one of the static constants defined in this class + * @return the name of the shape + */ + public static String typeName(int type) { + String name = (String)types.get(new Integer(type)); + return name; + } + + public static HashMap types; + static { + types = new HashMap(); + try { + Field[] f = ShapeTypes.class.getFields(); + for (int i = 0; i < f.length; i++){ + Object val = f[i].get(null); + if (val instanceof Integer) { + types.put(val, f[i].getName()); + } + } + } catch (IllegalAccessException e){ + throw new RuntimeException("Failed to initialize shape types"); + } + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java index ca5c1a565e..f0834578e7 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java @@ -19,7 +19,14 @@ 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.hslf.record.*; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.Vector; /** @@ -40,6 +47,11 @@ public abstract class Sheet * Returns the sheet number */ public abstract int getSheetNumber(); + + /** + * Fetch the PPDrawing from the underlying record + */ + protected abstract PPDrawing getPPDrawing(); /** * For a given PPDrawing, grab all the TextRuns @@ -106,4 +118,49 @@ public abstract class Sheet } } + /** + * Returns all shapes contained in this Sheet + * + * @return all shapes contained in this Sheet (Slide or Notes) + */ + public Shape[] getShapes() { + PPDrawing ppdrawing = getPPDrawing(); + + EscherContainerRecord dg = (EscherContainerRecord)ppdrawing.getEscherRecords()[0]; + EscherContainerRecord spgr = null; + List ch = dg.getChildRecords(); + + for (Iterator it = ch.iterator(); it.hasNext();) { + EscherRecord rec = (EscherRecord)it.next(); + if (rec.getRecordId() == EscherContainerRecord.SPGR_CONTAINER){ + spgr = (EscherContainerRecord)rec; + break; + } + } + ch = spgr.getChildRecords(); + + ArrayList shapes = new ArrayList(); + for (int i=1;itrue if the Line is inside a group, false otherwise + * @return the record container which holds this shape + */ + protected EscherContainerRecord create(boolean isChild) { + EscherContainerRecord spContainer = new EscherContainerRecord(); + spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER ); + //spContainer.setOptions((short)15); + + EscherSpRecord sp = new EscherSpRecord(); + int flags = EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE; + if (isChild) flags |= EscherSpRecord.FLAG_CHILD; + sp.setFlags(flags); + spContainer.addChildRecord(sp); + + EscherOptRecord opt = new EscherOptRecord(); + opt.setRecordId(EscherOptRecord.RECORD_ID); + spContainer.addChildRecord(opt); + + EscherRecord anchor; + if(isChild) anchor = new EscherChildAnchorRecord(); + else { + anchor = new EscherClientAnchorRecord(); + + //hack. internal variable EscherClientAnchorRecord.shortRecord can be + //initialized only in fillFields(). We need to set shortRecord=false; + byte[] header = new byte[16]; + LittleEndian.putUShort(header, 0, 0); + LittleEndian.putUShort(header, 2, 0); + LittleEndian.putInt(header, 4, 8); + anchor.fillFields(header, 0, null); + } + spContainer.addChildRecord(anchor); + + return spContainer; + } + + /** + * Returns width of the line in in points + */ + public double getLineWidth(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH); + return prop == null ? 0 : (double)prop.getPropertyValue()/EMU_PER_POINT; + } + + /** + * Sets the width of line in in points + * @param width the width of line in in points + */ + public void setLineWidth(double width){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, EscherProperties.LINESTYLE__LINEWIDTH, (int)(width*EMU_PER_POINT)); + } + + /** + * Sets the color of line + * + * @param color new color of the line + */ + public void setLineColor(Color color){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB(); + setEscherProperty(opt, EscherProperties.LINESTYLE__COLOR, rgb); + } + + /** + * @return color of the line + */ + public Color getLineColor(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherRGBProperty prop = (EscherRGBProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__COLOR); + Color color = null; + if (prop != null){ + Color swp = new Color(prop.getRgbColor()); + color = new Color(swp.getBlue(), swp.getGreen(), swp.getRed()); + } + return color; + } + + /** + * Sets line style. One of the constants defined in this class. + * + * @param style new style of the line. + */ + public void setLineStyle(int style){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, EscherProperties.LINESTYLE__LINEDASHING, style == Line.LineSolid ? -1 : style); + } + + /** + * Returns line style. One of the constants defined in this class. + * + * @return style of the line. + */ + public int getLineStyle(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__LINEDASHING); + return prop == null ? Line.LineSolid : prop.getPropertyValue(); + } + + public void setFillColor(Color color){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB(); + setEscherProperty(opt, EscherProperties.FILL__FILLCOLOR, rgb); + setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 1376273); + } + + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java b/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java index 5719c7e8ab..6f374ec050 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java @@ -86,7 +86,13 @@ public class Slide extends Sheet _runs[i] = _otherRuns[k]; } } - + + /** + * Create a new Slide instance + */ + public Slide(){ + _slide = new org.apache.poi.hslf.record.Slide(); + } /** * Sets the Notes that are associated with this. Updates the @@ -129,4 +135,6 @@ public class Slide extends Sheet * Returns the Notes Sheet for this slide, or null if there isn't one */ public Notes getNotesSheet() { return _notes; } + + protected PPDrawing getPPDrawing() { return _slide.getPPDrawing(); } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ColorSchemeAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ColorSchemeAtom.java index 61c67ae61c..c0b9bf4ec0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ColorSchemeAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ColorSchemeAtom.java @@ -118,6 +118,27 @@ public class ColorSchemeAtom extends RecordAtom accentAndHyperlinkColourRGB = (int)LittleEndian.getInt(source,start+8+24); accentAndFollowingHyperlinkColourRGB = (int)LittleEndian.getInt(source,start+8+28); } + + /** + * Create a new ColorSchemeAtom, to go with a new Slide + */ + public ColorSchemeAtom(){ + _header = new byte[8]; + LittleEndian.putUShort(_header, 0, 16); + LittleEndian.putUShort(_header, 2, (int)_type); + LittleEndian.putInt(_header, 4, 32); + + // Setup the default rgb values + backgroundColourRGB = 16777215; + textAndLinesColourRGB = 0; + shadowsColourRGB = 8421504; + titleTextColourRGB = 0; + fillsColourRGB = 10079232; + accentColourRGB = 13382451; + accentAndHyperlinkColourRGB = 16764108; + accentAndFollowingHyperlinkColourRGB = 11711154; + } + /** * We are of type 3999 diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java b/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java index 44b584e755..d11ba429e4 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java @@ -81,8 +81,8 @@ public class FontCollection extends RecordContainer { fnt.setFontName(name); fonts.add(name); - // append new child to the end - _children = appendChildRecord(fnt,_children); + // Append new child to the end + appendChildRecord(fnt); return fonts.size()-1; //the added font is the last in the list } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java index e19bc0a994..4cbcca00bf 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java @@ -21,6 +21,7 @@ package org.apache.poi.hslf.record; import org.apache.poi.util.LittleEndian; import org.apache.poi.ddf.*; +import org.apache.poi.hslf.model.ShapeTypes; import java.io.IOException; import java.io.OutputStream; @@ -97,6 +98,20 @@ public class PPDrawing extends RecordAtom textboxWrappers[i] = (EscherTextboxWrapper)textboxes.get(i); } } + + /** + * Creates a new, empty, PPDrawing (typically for use with a new Slide + * or Notes) + */ + public PPDrawing(){ + _header = new byte[8]; + LittleEndian.putUShort(_header, 0, 15); + LittleEndian.putUShort(_header, 2, (int)RecordTypes.PPDrawing.typeID); + LittleEndian.putInt(_header, 4, 0); + + textboxWrappers = new EscherTextboxWrapper[]{}; + create(); + } /** * Tree walking way of finding Escher Child Records @@ -188,4 +203,63 @@ public class PPDrawing extends RecordAtom // Finally, write out the children out.write(b); } + + /** + * Create the Escher records associated with a new PPDrawing + */ + private void create(){ + EscherContainerRecord dgContainer = new EscherContainerRecord(); + dgContainer.setRecordId( EscherContainerRecord.DG_CONTAINER ); + dgContainer.setOptions((short)15); + + EscherDgRecord dg = new EscherDgRecord(); + dg.setOptions((short)16); + dg.setNumShapes(1); + dgContainer.addChildRecord(dg); + + EscherContainerRecord spgrContainer = new EscherContainerRecord(); + spgrContainer.setOptions((short)15); + spgrContainer.setRecordId(EscherContainerRecord.SPGR_CONTAINER); + + EscherContainerRecord spContainer = new EscherContainerRecord(); + spContainer.setOptions((short)15); + spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER); + + EscherSpgrRecord spgr = new EscherSpgrRecord(); + spgr.setOptions((short)1); + spContainer.addChildRecord(spgr); + + EscherSpRecord sp = new EscherSpRecord(); + sp.setOptions((short)((ShapeTypes.NotPrimitive << 4) + 2)); + sp.setFlags(EscherSpRecord.FLAG_PATRIARCH | EscherSpRecord.FLAG_GROUP); + spContainer.addChildRecord(sp); + spgrContainer.addChildRecord(spContainer); + dgContainer.addChildRecord(spgrContainer); + + spContainer = new EscherContainerRecord(); + spContainer.setOptions((short)15); + spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER); + sp = new EscherSpRecord(); + sp.setOptions((short)((ShapeTypes.Rectangle << 4) + 2)); + sp.setFlags(EscherSpRecord.FLAG_BACKGROUND | EscherSpRecord.FLAG_HASSHAPETYPE); + spContainer.addChildRecord(sp); + + EscherOptRecord opt = new EscherOptRecord(); + opt.setRecordId(EscherOptRecord.RECORD_ID); + opt.addEscherProperty(new EscherRGBProperty(EscherProperties.FILL__FILLCOLOR, 134217728)); + opt.addEscherProperty(new EscherRGBProperty(EscherProperties.FILL__FILLBACKCOLOR, 134217733)); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.FILL__RECTRIGHT, 10064750)); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.FILL__RECTBOTTOM, 7778750)); + opt.addEscherProperty(new EscherBoolProperty(EscherProperties.FILL__NOFILLHITTEST, 1179666)); + opt.addEscherProperty(new EscherBoolProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 524288)); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.SHAPE__BLACKANDWHITESETTINGS, 9)); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.SHAPE__BACKGROUNDSHAPE, 65537)); + spContainer.addChildRecord(opt); + + dgContainer.addChildRecord(spContainer); + + childRecords = new EscherRecord[]{ + dgContainer + }; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java b/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java index cb5b5dee21..34671ee4a3 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java @@ -77,6 +77,13 @@ public class PersistPtrHolder extends PositionDependentRecordAtom public Hashtable getSlideLocationsLookup() { return _slideLocations; } + /** + * Get the lookup from slide numbers to their offsets inside + * _ptrData, used when adding or moving slides. + */ + public Hashtable getSlideOffsetDataLocationsLookup() { + return _slideOffsetDataLocation; + } /** * Adds a new slide, notes or similar, to be looked up by this. @@ -104,6 +111,10 @@ public class PersistPtrHolder extends PositionDependentRecordAtom // Update the atom header LittleEndian.putInt(_header,4,newPtrData.length); + + // Update info (first 4 bytes in ptr data) + int info = (slideID << 20 | 1); + LittleEndian.putInt(_ptrData, 0, info); } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java index cd7aa16b32..996f929093 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java @@ -48,17 +48,12 @@ public abstract class RecordContainer extends Record public boolean isAnAtom() { return false; } /** - * Add a new child record onto a record's list of children, and - * return the new list. + * Add a new child record onto a record's list of children. */ - public Record[] appendChildRecord(Record newChild, Record[] children) { - Record[] r; + public void appendChildRecord(Record newChild) { synchronized(addingChildRecordLock) { - r = new Record[children.length + 1]; - System.arraycopy(children,0,r,0,children.length); - r[r.length-1] = newChild; + addChildAt(newChild, _children.length); } - return r; } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Slide.java b/src/scratchpad/src/org/apache/poi/hslf/record/Slide.java index 9717a58c2e..06262909ad 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/Slide.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/Slide.java @@ -18,10 +18,10 @@ package org.apache.poi.hslf.record; -import org.apache.poi.util.LittleEndian; import java.io.IOException; import java.io.OutputStream; -import java.io.ByteArrayOutputStream; + +import org.apache.poi.util.LittleEndian; /** * Master container for Slides. There is one of these for every slide, @@ -73,6 +73,27 @@ public class Slide extends PositionDependentRecordContainer } } + /** + * Create a new, empty, Slide, along with its required + * child records. + */ + public Slide(){ + _header = new byte[8]; + LittleEndian.putUShort(_header, 0, 15); + LittleEndian.putUShort(_header, 2, (int)_type); + LittleEndian.putInt(_header, 4, 0); + + slideAtom = new SlideAtom(); + ppDrawing = new PPDrawing(); + + ColorSchemeAtom colorAtom = new ColorSchemeAtom(); + + _children = new Record[] { + slideAtom, + ppDrawing, + colorAtom + }; + } /** * We are of type 1006 diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java index 677b3d8592..f1c4bcb684 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java @@ -111,6 +111,27 @@ public class SlideAtom extends RecordAtom reserved = new byte[len-30]; System.arraycopy(source,start+30,reserved,0,reserved.length); } + + /** + * Create a new SlideAtom, to go with a new Slide + */ + public SlideAtom(){ + _header = new byte[8]; + LittleEndian.putUShort(_header, 0, 2); + LittleEndian.putUShort(_header, 2, (int)_type); + LittleEndian.putInt(_header, 4, 24); + + byte[] ssdate = new byte[12]; + layoutAtom = new SSlideLayoutAtom(ssdate); + layoutAtom.setGeometryType(SSlideLayoutAtom.BLANK_SLIDE); + + followMasterObjects = true; + followMasterScheme = true; + followMasterBackground = true; + masterID = -2147483648; + notesID = 0; + reserved = new byte[2]; + } /** * We are of type 1007 @@ -180,6 +201,8 @@ public class SlideAtom extends RecordAtom /** Retrieve the geometry type */ public int getGeometryType() { return geometry; } + /** Set the geometry type */ + public void setGeometryType(int geom) { geometry = geom; } /** * Create a new Embeded SSlideLayoutAtom, from 12 bytes of data diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java index ca1f88ff24..560dd1e5b0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java @@ -97,6 +97,17 @@ public class SlideListWithText extends RecordContainer } } + /** + * Create a new, empty, SlideListWithText + */ + public SlideListWithText(){ + _header = new byte[8]; + LittleEndian.putUShort(_header, 0, 15); + LittleEndian.putUShort(_header, 2, (int)_type); + LittleEndian.putInt(_header, 4, 0); + + _children = new Record[0]; + } /** * Get access to the SlideAtomsSets of the children of this record diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java index f52bfbfc2a..7c93494fc2 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java @@ -48,6 +48,14 @@ public class SlidePersistAtom extends RecordAtom public int getSlideIdentifier() { return slideIdentifier; } public int getNumPlaceholderTexts() { return numPlaceholderTexts; } public boolean getHasShapesOtherThanPlaceholders() { return hasShapesOtherThanPlaceholders; } + + // Only set these if you know what you're doing! + public void setRefID(int id) { + refID = id; + } + public void setSlideIdentifier(int id) { + slideIdentifier = id; + } /* *************** record code follows ********************** */ @@ -84,6 +92,19 @@ public class SlidePersistAtom extends RecordAtom reservedFields = new byte[len-24]; System.arraycopy(source,start+24,reservedFields,0,reservedFields.length); } + + /** + * Create a new SlidePersistAtom, for use with a new Slide + */ + public SlidePersistAtom(){ + _header = new byte[8]; + LittleEndian.putUShort(_header, 0, 0); + LittleEndian.putUShort(_header, 2, (int)_type); + LittleEndian.putInt(_header, 4, 20); + + hasShapesOtherThanPlaceholders = true; + reservedFields = new byte[4]; + } /** * We are of type 1011 diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java index 2bc61f8577..534dabfce8 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java @@ -66,6 +66,7 @@ public class UserEditAtom extends PositionDependentRecordAtom // More scary internal setters public void setLastUserEditAtomOffset(int offset) { lastUserEditAtomOffset = offset; } public void setPersistPointersOffset(int offset) { persistPointersOffset = offset; } + public void setLastViewType(short type) { lastViewType=type; } /* *************** record code follows ********************** */ diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java index ac16539c40..c47294495a 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java @@ -34,6 +34,8 @@ import org.apache.poi.hslf.record.RecordContainer; import org.apache.poi.hslf.record.RecordTypes; import org.apache.poi.hslf.record.SlideAtom; import org.apache.poi.hslf.record.SlideListWithText; +import org.apache.poi.hslf.record.SlidePersistAtom; +import org.apache.poi.hslf.record.UserEditAtom; import org.apache.poi.hslf.record.SlideListWithText.*; import org.apache.poi.hslf.record.PersistPtrHolder; import org.apache.poi.hslf.record.PositionDependentRecord; @@ -378,74 +380,94 @@ public class SlideShow * @throws IOException */ public Slide createSlide() throws IOException { -// RecordContainer slist=null; -// Record[] rec = doc.getChildRecords(); -// int num = 0; -// for (int i = 0; i < rec.length; i++) { -// Record record = rec[i]; -// if (record.getRecordType() == RecordTypes.SlideListWithText.typeID){ -// if (num > 0){ -// slist = (RecordContainer)record; -// } -// num++; -// } -// } -// if (num == 1){ -// slist = new SlideListWithText(); -// rec = doc.getChildRecords(); -// for (int i = 0; i < rec.length-1; i++) { -// Record record = rec[i+1]; -// if (record.getRecordType() == RecordTypes.EndDocument.typeID){ -// -// doc.addChildAfter(slist, rec[i]); -// } -// } -// } -// rec = slist.getChildRecords(); -// -// //add SlidePersistAtom -// SlidePersistAtom prev = rec.length == 0 ? null : (SlidePersistAtom)rec[rec.length - 1]; -// SlidePersistAtom sp = new SlidePersistAtom(); -// -// //refernce is the 1-based index of the slide container in the document root. -// //it always starts with 3 (1 is Document, 2 is MainMaster, 3 is the first slide) -// sp.setRefID(prev == null ? 3 : (prev.getRefID() + 1)); -// //first slideId is always 256 -// sp.setSlideIdentifier(prev == null ? 256 : (prev.getSlideIdentifier() + 1)); -// -// Record[] r = slist.appendChildRecord(sp, -// slist.getChildRecords() == null ? new Record[]{} : slist.getChildRecords()); -// slist.setChildRecords(r); -// Slide slide = new Slide(); -// -// int offset = 0; -// List lst = new ArrayList(); -// for (int i = 0; i < _records.length; i++) { -// Record record = _records[i]; -// lst.add(record); -// ByteArrayOutputStream out = new ByteArrayOutputStream(); -// record.writeOut(out); -// -// if (_records[i].getRecordType() == RecordTypes.PersistPtrIncrementalBlock.typeID){ -// lst.add(i, slide.getSlideRecord()); -// -// slide.getSlideRecord().setLastOnDiskOffset(offset); -// PersistPtrHolder ptr = (PersistPtrHolder)_records[i]; -// int id = sp.getRefID(); -// ptr.getSlideDataLocationsLookup().put(new Integer(id), new Integer((i+1)*4)); -// ptr.getSlideLocationsLookup().put(new Integer(id), new Integer(offset)); -// ptr.addSlideLookup(id, offset); -// -// } -// offset += out.size() ; -// } -// _records = (Record[])lst.toArray(new Record[lst.size()]); -// _hslfSlideShow.setRecords(_records); -// -// UserEditAtom usr = (UserEditAtom)_records[_records.length-1]; -// usr.setLastViewType((short)UserEditAtom.LAST_VIEW_SLIDE_VIEW); -// return slide; - return null; + SlideListWithText[] slwts = _documentRecord.getSlideListWithTexts(); + SlideListWithText slist = null; + + if(slwts.length > 1) { + // Just use the last one + slist = slwts[slwts.length - 1]; + } else { + // Need to add a new one + slist = new SlideListWithText(); + + // Goes in just before the EndDocumentRecord + Record[] docChildren = _documentRecord.getChildRecords(); + Record endDoc = docChildren[docChildren.length - 1]; + if(endDoc.getRecordType() != RecordTypes.EndDocument.typeID) { + throw new IllegalStateException("The last child record of a Document should be EndDocument, but it was " + endDoc); + } + _documentRecord.addChildBefore(slist, endDoc); + } + + Record[] rec = slist.getChildRecords(); + + // Add SlidePersistAtom + SlidePersistAtom prev = rec.length == 0 ? null : (SlidePersistAtom)rec[rec.length - 1]; + SlidePersistAtom sp = new SlidePersistAtom(); + + // Refernce is the 1-based index of the slide container in + // the document root. + // It always starts with 3 (1 is Document, 2 is MainMaster, 3 is + // the first slide) + sp.setRefID(prev == null ? 3 : (prev.getRefID() + 1)); + // First slideId is always 256 + sp.setSlideIdentifier(prev == null ? 256 : (prev.getSlideIdentifier() + 1)); + + slist.appendChildRecord(sp); + + // Create a new Slide + Slide slide = new Slide(); + Slide[] s = new Slide[_slides.length+1]; + System.arraycopy(_slides, 0, s, 0, _slides.length); + s[_slides.length] = slide; + _slides = s; + System.out.println("Added slide " + _slides.length + " with ref " + sp.getRefID() + " and identifier " + sp.getSlideIdentifier()); + + // Add in to the core records + org.apache.poi.hslf.record.Slide slideRecord = slide.getSlideRecord(); + int slideRecordPos = _hslfSlideShow.appendRootLevelRecord(slideRecord); + + // Add the new Slide into the PersistPtr stuff + int offset = 0; + int slideOffset = 0; + PersistPtrHolder ptr = null; + UserEditAtom usr = null; + for (int i = 0; i < _records.length; i++) { + 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(); + } + + // Add the new slide into the last PersistPtr + slideRecord.setLastOnDiskOffset(slideOffset); + int id = sp.getRefID(); + ptr.getSlideOffsetDataLocationsLookup().put( + new Integer(id), + new Integer((slideRecordPos+1)*4) + ); + ptr.getSlideLocationsLookup().put( + new Integer(id), new Integer(slideOffset)); + ptr.addSlideLookup(id, slideOffset); + System.out.println("New slide ended up at " + slideOffset); + + // Last view is now of the slide + usr.setLastViewType((short)UserEditAtom.LAST_VIEW_SLIDE_VIEW); + + // All done and added + return slide; } -- 2.39.5