001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.hslf.record;
019:
020: import org.apache.poi.hslf.model.textproperties.*;
021: import org.apache.poi.util.LittleEndian;
022: import org.apache.poi.util.POILogger;
023:
024: import java.io.IOException;
025: import java.io.OutputStream;
026: import java.io.ByteArrayOutputStream;
027: import java.util.LinkedList;
028: import java.util.Vector;
029: import java.util.List;
030: import java.util.Iterator;
031:
032: /**
033: * A StyleTextPropAtom (type 4001). Holds basic character properties
034: * (bold, italic, underline, font size etc) and paragraph properties
035: * (alignment, line spacing etc) for the block of text (TextBytesAtom
036: * or TextCharsAtom) that this record follows.
037: * You will find two lists within this class.
038: * 1 - Paragraph style list (paragraphStyles)
039: * 2 - Character style list (charStyles)
040: * Both are lists of TextPropCollections. These define how many characters
041: * the style applies to, and what style elements make up the style (another
042: * list, this time of TextProps). Each TextProp has a value, which somehow
043: * encapsulates a property of the style
044: *
045: * @author Nick Burch
046: * @author Yegor Kozlov
047: */
048:
049: public class StyleTextPropAtom extends RecordAtom {
050: private byte[] _header;
051: private static long _type = 4001l;
052: private byte[] reserved;
053:
054: private byte[] rawContents; // Holds the contents between write-outs
055:
056: /**
057: * Only set to true once setParentTextSize(int) is called.
058: * Until then, no stylings will have been decoded
059: */
060: private boolean initialised = false;
061:
062: /**
063: * The list of all the different paragraph stylings we code for.
064: * Each entry is a TextPropCollection, which tells you how many
065: * Characters the paragraph covers, and also contains the TextProps
066: * that actually define the styling of the paragraph.
067: */
068: private LinkedList paragraphStyles;
069:
070: public LinkedList getParagraphStyles() {
071: return paragraphStyles;
072: }
073:
074: /**
075: * Updates the link list of TextPropCollections which make up the
076: * paragraph stylings
077: */
078: public void setParagraphStyles(LinkedList ps) {
079: paragraphStyles = ps;
080: }
081:
082: /**
083: * The list of all the different character stylings we code for.
084: * Each entry is a TextPropCollection, which tells you how many
085: * Characters the character styling covers, and also contains the
086: * TextProps that actually define the styling of the characters.
087: */
088: private LinkedList charStyles;
089:
090: public LinkedList getCharacterStyles() {
091: return charStyles;
092: }
093:
094: /**
095: * Updates the link list of TextPropCollections which make up the
096: * character stylings
097: */
098: public void setCharacterStyles(LinkedList cs) {
099: charStyles = cs;
100: }
101:
102: /** All the different kinds of paragraph properties we might handle */
103: public static TextProp[] paragraphTextPropTypes = new TextProp[] {
104: new ParagraphFlagsTextProp(),
105: new TextProp(2, 0x80, "bullet.char"),
106: new TextProp(2, 0x10, "bullet.font"),
107: new TextProp(4, 0x20, "bullet.color"),
108: new TextProp(2, 0x40, "bullet.size"),
109: new AlignmentTextProp(),
110: new TextProp(2, 0x100, "text.offset"),
111: new TextProp(2, 0x200, "para_unknown_2"),
112: new TextProp(2, 0x400, "bullet.offset"),
113: new TextProp(2, 0x1000, "linespacing"),
114: new TextProp(2, 0x2000, "spacebefore"),
115: new TextProp(2, 0x4000, "spaceafter"),
116: new TextProp(2, 0x8000, "para_unknown_4"),
117: new TextProp(2, 0x10000, "para_unknown_5"),
118: new TextProp(2, 0xE0000, "para_unknown_6"),
119: new TextProp(2, 0x200000, "para_unknown_7") };
120: /** All the different kinds of character properties we might handle */
121: public static TextProp[] characterTextPropTypes = new TextProp[] {
122: new CharFlagsTextProp(),
123: new TextProp(2, 0x10000, "font.index"),
124: new TextProp(2, 0x200000, "asian_or_complex"),
125: new TextProp(2, 0x400000, "char_unknown_2"),
126: new TextProp(2, 0x800000, "symbol"),
127: new TextProp(2, 0x20000, "font.size"),
128: new TextProp(4, 0x40000, "font.color"),
129: new TextProp(2, 0x80000, "offset"),
130: new TextProp(2, 0x100000, "char_unknown_1"),
131: new TextProp(2, 0x1000000, "char_unknown_3"),
132: new TextProp(2, 0x2000000, "char_unknown_4"),
133: new TextProp(2, 0x4000000, "char_unknown_5"),
134: new TextProp(2, 0x8000000, "char_unknown_6"),
135: new TextProp(2, 0x10000000, "char_unknown_7"),
136: new TextProp(2, 0x20000000, "char_unknown_8"),
137: new TextProp(2, 0x40000000, "char_unknown_9"),
138: new TextProp(2, 0x80000000, "char_unknown_10"), };
139:
140: /* *************** record code follows ********************** */
141:
142: /**
143: * For the Text Style Properties (StyleTextProp) Atom
144: */
145: protected StyleTextPropAtom(byte[] source, int start, int len) {
146: // Sanity Checking - we're always at least 8+10 bytes long
147: if (len < 18) {
148: len = 18;
149: if (source.length - start < 18) {
150: throw new RuntimeException(
151: "Not enough data to form a StyleTextPropAtom (min size 18 bytes long) - found "
152: + (source.length - start));
153: }
154: }
155:
156: // Get the header
157: _header = new byte[8];
158: System.arraycopy(source, start, _header, 0, 8);
159:
160: // Save the contents of the atom, until we're asked to go and
161: // decode them (via a call to setParentTextSize(int)
162: rawContents = new byte[len - 8];
163: System.arraycopy(source, start + 8, rawContents, 0,
164: rawContents.length);
165: reserved = new byte[0];
166:
167: // Set empty linked lists, ready for when they call setParentTextSize
168: paragraphStyles = new LinkedList();
169: charStyles = new LinkedList();
170: }
171:
172: /**
173: * A new set of text style properties for some text without any.
174: */
175: public StyleTextPropAtom(int parentTextSize) {
176: _header = new byte[8];
177: rawContents = new byte[0];
178: reserved = new byte[0];
179:
180: // Set our type
181: LittleEndian.putInt(_header, 2, (short) _type);
182: // Our initial size is 10
183: LittleEndian.putInt(_header, 4, 10);
184:
185: // Set empty paragraph and character styles
186: paragraphStyles = new LinkedList();
187: charStyles = new LinkedList();
188:
189: TextPropCollection defaultParagraphTextProps = new TextPropCollection(
190: parentTextSize, (short) 0);
191: paragraphStyles.add(defaultParagraphTextProps);
192:
193: TextPropCollection defaultCharacterTextProps = new TextPropCollection(
194: parentTextSize);
195: charStyles.add(defaultCharacterTextProps);
196:
197: // Set us as now initialised
198: initialised = true;
199: }
200:
201: /**
202: * We are of type 4001
203: */
204: public long getRecordType() {
205: return _type;
206: }
207:
208: /**
209: * Write the contents of the record back, so it can be written
210: * to disk
211: */
212: public void writeOut(OutputStream out) throws IOException {
213: // First thing to do is update the raw bytes of the contents, based
214: // on the properties
215: updateRawContents();
216:
217: // Now ensure that the header size is correct
218: int newSize = rawContents.length + reserved.length;
219: LittleEndian.putInt(_header, 4, newSize);
220:
221: // Write out the (new) header
222: out.write(_header);
223:
224: // Write out the styles
225: out.write(rawContents);
226:
227: // Write out any extra bits
228: out.write(reserved);
229: }
230:
231: /**
232: * Tell us how much text the parent TextCharsAtom or TextBytesAtom
233: * contains, so we can go ahead and initialise ourselves.
234: */
235: public void setParentTextSize(int size) {
236: int pos = 0;
237: int textHandled = 0;
238:
239: // While we have text in need of paragraph stylings, go ahead and
240: // grok the contents as paragraph formatting data
241: int prsize = size;
242: while (pos < rawContents.length && textHandled < prsize) {
243: // First up, fetch the number of characters this applies to
244: int textLen = LittleEndian.getInt(rawContents, pos);
245: textHandled += textLen;
246: pos += 4;
247:
248: // Fetch the 2 byte value that is safe to ignore as 0
249: short paraIgn = LittleEndian.getShort(rawContents, pos);
250: pos += 2;
251:
252: // Grab the 4 byte value that tells us what properties follow
253: int paraFlags = LittleEndian.getInt(rawContents, pos);
254: pos += 4;
255:
256: // Now make sense of those properties
257: TextPropCollection this Collection = new TextPropCollection(
258: textLen, paraIgn);
259: int plSize = this Collection.buildTextPropList(paraFlags,
260: paragraphTextPropTypes, rawContents, pos);
261: pos += plSize;
262:
263: // Save this properties set
264: paragraphStyles.add(this Collection);
265:
266: // Handle extra 1 paragraph styles at the end
267: if (pos < rawContents.length && textHandled == size) {
268: prsize++;
269: }
270:
271: }
272: if (rawContents.length > 0 && textHandled != (size + 1)) {
273: logger.log(POILogger.WARN,
274: "Problem reading paragraph style runs: textHandled = "
275: + textHandled + ", text.size+1 = "
276: + (size + 1));
277: }
278:
279: // Now do the character stylings
280: textHandled = 0;
281: int chsize = size;
282: while (pos < rawContents.length && textHandled < chsize) {
283: // First up, fetch the number of characters this applies to
284: int textLen = LittleEndian.getInt(rawContents, pos);
285: textHandled += textLen;
286: pos += 4;
287:
288: // There is no 2 byte value
289: short no_val = -1;
290:
291: // Grab the 4 byte value that tells us what properties follow
292: int charFlags = LittleEndian.getInt(rawContents, pos);
293: pos += 4;
294:
295: // Now make sense of those properties
296: // (Assuming we actually have some)
297: TextPropCollection this Collection = new TextPropCollection(
298: textLen, no_val);
299: int chSize = this Collection.buildTextPropList(charFlags,
300: characterTextPropTypes, rawContents, pos);
301: pos += chSize;
302:
303: // Save this properties set
304: charStyles.add(this Collection);
305:
306: // Handle extra 1 char styles at the end
307: if (pos < rawContents.length && textHandled == size) {
308: chsize++;
309: }
310: }
311: if (rawContents.length > 0 && textHandled != (size + 1)) {
312: logger.log(POILogger.WARN,
313: "Problem reading character style runs: textHandled = "
314: + textHandled + ", text.size+1 = "
315: + (size + 1));
316: }
317:
318: // Handle anything left over
319: if (pos < rawContents.length) {
320: reserved = new byte[rawContents.length - pos];
321: System.arraycopy(rawContents, pos, reserved, 0,
322: reserved.length);
323: }
324:
325: initialised = true;
326: }
327:
328: /**
329: * Updates the cache of the raw contents. Serialised the styles out.
330: */
331: private void updateRawContents() throws IOException {
332: if (!initialised) {
333: // We haven't groked the styles since creation, so just stick
334: // with what we found
335: return;
336: }
337:
338: ByteArrayOutputStream baos = new ByteArrayOutputStream();
339:
340: // First up, we need to serialise the paragraph properties
341: for (int i = 0; i < paragraphStyles.size(); i++) {
342: TextPropCollection tpc = (TextPropCollection) paragraphStyles
343: .get(i);
344: tpc.writeOut(baos);
345: }
346:
347: // Now, we do the character ones
348: for (int i = 0; i < charStyles.size(); i++) {
349: TextPropCollection tpc = (TextPropCollection) charStyles
350: .get(i);
351: tpc.writeOut(baos);
352: }
353:
354: rawContents = baos.toByteArray();
355: }
356:
357: /**
358: * Create a new Paragraph TextPropCollection, and add it to the list
359: * @param charactersCovered The number of characters this TextPropCollection will cover
360: * @return the new TextPropCollection, which will then be in the list
361: */
362: public TextPropCollection addParagraphTextPropCollection(
363: int charactersCovered) {
364: TextPropCollection tpc = new TextPropCollection(
365: charactersCovered, (short) 0);
366: paragraphStyles.add(tpc);
367: return tpc;
368: }
369:
370: /**
371: * Create a new Character TextPropCollection, and add it to the list
372: * @param charactersCovered The number of characters this TextPropCollection will cover
373: * @return the new TextPropCollection, which will then be in the list
374: */
375: public TextPropCollection addCharacterTextPropCollection(
376: int charactersCovered) {
377: TextPropCollection tpc = new TextPropCollection(
378: charactersCovered);
379: charStyles.add(tpc);
380: return tpc;
381: }
382:
383: /* ************************************************************************ */
384:
385: /**
386: * Dump the record content into <code>StringBuffer</code>
387: *
388: * @return the string representation of the record data
389: */
390: public String toString() {
391: StringBuffer out = new StringBuffer();
392: out.append("Paragraph properties\n");
393: for (Iterator it1 = getParagraphStyles().iterator(); it1
394: .hasNext();) {
395: TextPropCollection pr = (TextPropCollection) it1.next();
396: out.append(" chars covered: " + pr.getCharactersCovered()
397: + "\n");
398: for (Iterator it2 = pr.getTextPropList().iterator(); it2
399: .hasNext();) {
400: TextProp p = (TextProp) it2.next();
401: out.append(" " + p.getName() + " = " + p.getValue()
402: + "\n");
403: }
404: }
405:
406: out.append("Character properties\n");
407: for (Iterator it1 = getCharacterStyles().iterator(); it1
408: .hasNext();) {
409: TextPropCollection pr = (TextPropCollection) it1.next();
410: out.append(" chars covered: " + pr.getCharactersCovered()
411: + "\n");
412: for (Iterator it2 = pr.getTextPropList().iterator(); it2
413: .hasNext();) {
414: TextProp p = (TextProp) it2.next();
415: out.append(" " + p.getName() + " = " + p.getValue()
416: + "\n");
417: }
418: }
419:
420: return out.toString();
421: }
422: }
|