1 /* ====================================================================
2 Licensed to the Apache Software Foundation (ASF) under one or more
3 contributor license agreements. See the NOTICE file distributed with
4 this work for additional information regarding copyright ownership.
5 The ASF licenses this file to You under the Apache License, Version 2.0
6 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 ==================================================================== */
17
18
19 package org.apache.poi.hslf.model;
20
21 import org.apache.poi.ddf;
22 import org.apache.poi.hslf.record;
23 import org.apache.poi.hslf.usermodel.SlideShow;
24 import org.apache.poi.util.POILogger;
25
26 import java.util.ArrayList;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Vector;
30 import java.awt;
31
32 /**
33 * This class defines the common format of "Sheets" in a powerpoint
34 * document. Such sheets could be Slides, Notes, Master etc
35 *
36 * @author Nick Burch
37 * @author Yegor Kozlov
38 */
39
40 public abstract class Sheet {
41 /**
42 * The <code>SlideShow</code> we belong to
43 */
44 private SlideShow _slideShow;
45
46 /**
47 * Sheet background
48 */
49 private Background _background;
50
51 /**
52 * Record container that holds sheet data.
53 * For slides it is org.apache.poi.hslf.record.Slide,
54 * for notes it is org.apache.poi.hslf.record.Notes,
55 * for slide masters it is org.apache.poi.hslf.record.SlideMaster, etc.
56 */
57 private SheetContainer _container;
58
59 private int _sheetNo;
60
61 public Sheet(SheetContainer container, int sheetNo) {
62 _container = container;
63 _sheetNo = sheetNo;
64 }
65
66 /**
67 * Returns an array of all the TextRuns in the sheet.
68 */
69 public abstract TextRun[] getTextRuns();
70
71 /**
72 * Returns the (internal, RefID based) sheet number, as used
73 * to in PersistPtr stuff.
74 */
75 public int _getSheetRefId() {
76 return _container.getSheetId();
77 }
78
79 /**
80 * Returns the (internal, SlideIdentifier based) sheet number, as used
81 * to reference this sheet from other records.
82 */
83 public int _getSheetNumber() {
84 return _sheetNo;
85 }
86
87 /**
88 * Fetch the PPDrawing from the underlying record
89 */
90 protected PPDrawing getPPDrawing() {
91 return _container.getPPDrawing();
92 }
93
94 /**
95 * Fetch the SlideShow we're attached to
96 */
97 public SlideShow getSlideShow() {
98 return _slideShow;
99 }
100
101 /**
102 * Return record container for this sheet
103 */
104 public SheetContainer getSheetContainer() {
105 return _container;
106 }
107
108 /**
109 * Set the SlideShow we're attached to.
110 * Also passes it on to our child RichTextRuns
111 */
112 public void setSlideShow(SlideShow ss) {
113 _slideShow = ss;
114 TextRun[] trs = getTextRuns();
115 if (trs != null) {
116 for (int i = 0; i < trs.length; i++) {
117 trs[i].supplySlideShow(_slideShow);
118 }
119 }
120 }
121
122
123 /**
124 * For a given PPDrawing, grab all the TextRuns
125 */
126 public static TextRun[] findTextRuns(PPDrawing ppdrawing) {
127 Vector runsV = new Vector();
128 EscherTextboxWrapper[] wrappers = ppdrawing.getTextboxWrappers();
129 for (int i = 0; i < wrappers.length; i++) {
130 int s1 = runsV.size();
131 findTextRuns(wrappers[i].getChildRecords(), runsV);
132 int s2 = runsV.size();
133 if (s2 != s1){
134 TextRun t = (TextRun) runsV.get(runsV.size()-1);
135 t.setShapeId(wrappers[i].getShapeId());
136 }
137 }
138 TextRun[] runs = new TextRun[runsV.size()];
139 for (int i = 0; i < runs.length; i++) {
140 runs[i] = (TextRun) runsV.get(i);
141 }
142 return runs;
143 }
144
145 /**
146 * Scans through the supplied record array, looking for
147 * a TextHeaderAtom followed by one of a TextBytesAtom or
148 * a TextCharsAtom. Builds up TextRuns from these
149 *
150 * @param records the records to build from
151 * @param found vector to add any found to
152 */
153 protected static void findTextRuns(Record[] records, Vector found) {
154 // Look for a TextHeaderAtom
155 for (int i = 0, slwtIndex=0; i < (records.length - 1); i++) {
156 if (records[i] instanceof TextHeaderAtom) {
157 TextRun trun = null;
158 TextHeaderAtom tha = (TextHeaderAtom) records[i];
159 StyleTextPropAtom stpa = null;
160
161 // Look for a subsequent StyleTextPropAtom
162 if (i < (records.length - 2)) {
163 if (records[i + 2] instanceof StyleTextPropAtom) {
164 stpa = (StyleTextPropAtom) records[i + 2];
165 }
166 }
167
168 // See what follows the TextHeaderAtom
169 if (records[i + 1] instanceof TextCharsAtom) {
170 TextCharsAtom tca = (TextCharsAtom) records[i + 1];
171 trun = new TextRun(tha, tca, stpa);
172 } else if (records[i + 1] instanceof TextBytesAtom) {
173 TextBytesAtom tba = (TextBytesAtom) records[i + 1];
174 trun = new TextRun(tha, tba, stpa);
175 } else if (records[i + 1].getRecordType() == 4001l) {
176 // StyleTextPropAtom - Safe to ignore
177 } else if (records[i + 1].getRecordType() == 4010l) {
178 // TextSpecInfoAtom - Safe to ignore
179 } else {
180 System.err.println("Found a TextHeaderAtom not followed by a TextBytesAtom or TextCharsAtom: Followed by " + records[i + 1].getRecordType());
181 }
182
183 if (trun != null) {
184 ArrayList lst = new ArrayList();
185 for (int j = i; j < records.length; j++) {
186 if(j > i && records[j] instanceof TextHeaderAtom) break;
187 lst.add(records[j]);
188 }
189 Record[] recs = new Record[lst.size()];
190 lst.toArray(recs);
191 trun._records = recs;
192 trun.setIndex(slwtIndex);
193
194 found.add(trun);
195 i++;
196 } else {
197 // Not a valid one, so skip on to next and look again
198 }
199 slwtIndex++;
200 }
201 }
202 }
203
204 /**
205 * Returns all shapes contained in this Sheet
206 *
207 * @return all shapes contained in this Sheet (Slide or Notes)
208 */
209 public Shape[] getShapes() {
210 PPDrawing ppdrawing = getPPDrawing();
211
212 EscherContainerRecord dg = (EscherContainerRecord) ppdrawing.getEscherRecords()[0];
213 EscherContainerRecord spgr = null;
214 List ch = dg.getChildRecords();
215
216 for (Iterator it = ch.iterator(); it.hasNext();) {
217 EscherRecord rec = (EscherRecord) it.next();
218 if (rec.getRecordId() == EscherContainerRecord.SPGR_CONTAINER) {
219 spgr = (EscherContainerRecord) rec;
220 break;
221 }
222 }
223 ch = spgr.getChildRecords();
224
225 ArrayList shapes = new ArrayList();
226 for (int i = 1; i < ch.size(); i++) {
227 EscherContainerRecord sp = (EscherContainerRecord) ch.get(i);
228 Shape sh = ShapeFactory.createShape(sp, null);
229 sh.setSheet(this);
230 shapes.add(sh);
231 }
232
233 return (Shape[]) shapes.toArray(new Shape[shapes.size()]);
234 }
235
236 /**
237 * Add a new Shape to this Slide
238 *
239 * @param shape - the Shape to add
240 */
241 public void addShape(Shape shape) {
242 PPDrawing ppdrawing = getPPDrawing();
243
244 EscherContainerRecord dgContainer = (EscherContainerRecord) ppdrawing.getEscherRecords()[0];
245 EscherContainerRecord spgr = (EscherContainerRecord) Shape.getEscherChild(dgContainer, EscherContainerRecord.SPGR_CONTAINER);
246 spgr.addChildRecord(shape.getSpContainer());
247
248 shape.setSheet(this);
249 shape.setShapeId(allocateShapeId());
250 shape.afterInsert(this);
251 }
252
253 /**
254 * Allocates new shape id for the new drawing group id.
255 *
256 * @return a new shape id.
257 */
258 public int allocateShapeId()
259 {
260 EscherDggRecord dgg = _slideShow.getDocumentRecord().getPPDrawingGroup().getEscherDggRecord();
261 EscherDgRecord dg = _container.getPPDrawing().getEscherDgRecord();
262
263 dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 );
264
265 // Add to existing cluster if space available
266 for (int i = 0; i < dgg.getFileIdClusters().length; i++)
267 {
268 EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i];
269 if (c.getDrawingGroupId() == dg.getDrawingGroupId() && c.getNumShapeIdsUsed() != 1024)
270 {
271 int result = c.getNumShapeIdsUsed() + (1024 * (i+1));
272 c.incrementShapeId();
273 dg.setNumShapes( dg.getNumShapes() + 1 );
274 dg.setLastMSOSPID( result );
275 if (result >= dgg.getShapeIdMax())
276 dgg.setShapeIdMax( result + 1 );
277 return result;
278 }
279 }
280
281 // Create new cluster
282 dgg.addCluster( dg.getDrawingGroupId(), 0, false );
283 dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId();
284 dg.setNumShapes( dg.getNumShapes() + 1 );
285 int result = (1024 * dgg.getFileIdClusters().length);
286 dg.setLastMSOSPID( result );
287 if (result >= dgg.getShapeIdMax())
288 dgg.setShapeIdMax( result + 1 );
289 return result;
290 }
291
292 /**
293 * Removes the specified shape from this sheet.
294 *
295 * @param shape shape to be removed from this sheet, if present.
296 * @return <tt>true</tt> if the shape was deleted.
297 */
298 public boolean removeShape(Shape shape) {
299 PPDrawing ppdrawing = getPPDrawing();
300
301 EscherContainerRecord dg = (EscherContainerRecord) ppdrawing.getEscherRecords()[0];
302 EscherContainerRecord spgr = null;
303
304 for (Iterator it = dg.getChildRecords().iterator(); it.hasNext();) {
305 EscherRecord rec = (EscherRecord) it.next();
306 if (rec.getRecordId() == EscherContainerRecord.SPGR_CONTAINER) {
307 spgr = (EscherContainerRecord) rec;
308 break;
309 }
310 }
311 if(spgr == null) return false;
312
313 List lst = spgr.getChildRecords();
314 return lst.remove(shape.getSpContainer());
315 }
316
317 /**
318 * Called by SlideShow ater a new sheet is created
319 */
320 public void onCreate(){
321
322 }
323
324 /**
325 * Return the master sheet .
326 */
327 public abstract MasterSheet getMasterSheet();
328
329 /**
330 * Color scheme for this sheet.
331 */
332 public ColorSchemeAtom getColorScheme() {
333 return _container.getColorScheme();
334 }
335
336 /**
337 * Returns the background shape for this sheet.
338 *
339 * @return the background shape for this sheet.
340 */
341 public Background getBackground() {
342 if (_background == null) {
343 PPDrawing ppdrawing = getPPDrawing();
344
345 EscherContainerRecord dg = (EscherContainerRecord) ppdrawing.getEscherRecords()[0];
346 EscherContainerRecord spContainer = null;
347 List ch = dg.getChildRecords();
348
349 for (Iterator it = ch.iterator(); it.hasNext();) {
350 EscherRecord rec = (EscherRecord) it.next();
351 if (rec.getRecordId() == EscherContainerRecord.SP_CONTAINER) {
352 spContainer = (EscherContainerRecord) rec;
353 break;
354 }
355 }
356 _background = new Background(spContainer, null);
357 _background.setSheet(this);
358 }
359 return _background;
360 }
361
362 public void draw(Graphics2D graphics){
363
364 }
365 }