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.hwpf.model;
019:
020: import java.util.*;
021: import java.io.IOException;
022:
023: import org.apache.poi.util.LittleEndian;
024: import org.apache.poi.hwpf.model.io.HWPFOutputStream;
025: import org.apache.poi.hwpf.usermodel.CharacterProperties;
026: import org.apache.poi.hwpf.usermodel.ParagraphProperties;
027: import org.apache.poi.hwpf.sprm.ParagraphSprmUncompressor;
028: import org.apache.poi.hwpf.sprm.CharacterSprmUncompressor;
029:
030: /**
031: * Represents a document's stylesheet. A word documents formatting is stored as
032: * compressed styles that are based on styles contained in the stylesheet. This
033: * class also contains static utility functions to uncompress different
034: * formatting properties.
035: *
036: * @author Ryan Ackley
037: */
038:
039: public class StyleSheet implements HDFType {
040:
041: public static final int NIL_STYLE = 4095;
042: private static final int PAP_TYPE = 1;
043: private static final int CHP_TYPE = 2;
044: private static final int SEP_TYPE = 4;
045: private static final int TAP_TYPE = 5;
046:
047: private final static ParagraphProperties NIL_PAP = new ParagraphProperties();
048: private final static CharacterProperties NIL_CHP = new CharacterProperties();
049:
050: private int _stshiLength;
051: private int _baseLength;
052: private int _flags;
053: private int _maxIndex;
054: private int _maxFixedIndex;
055: private int _stylenameVersion;
056: private int[] _rgftc;
057:
058: StyleDescription[] _styleDescriptions;
059:
060: /**
061: * StyleSheet constructor. Loads a document's stylesheet information,
062: *
063: * @param tableStream A byte array containing a document's raw stylesheet
064: * info. Found by using FileInformationBlock.getFcStshf() and
065: * FileInformationBLock.getLcbStshf()
066: */
067: public StyleSheet(byte[] tableStream, int offset) {
068: _stshiLength = LittleEndian.getShort(tableStream, offset);
069: offset += LittleEndian.SHORT_SIZE;
070: int stdCount = LittleEndian.getShort(tableStream, offset);
071: offset += LittleEndian.SHORT_SIZE;
072: _baseLength = LittleEndian.getShort(tableStream, offset);
073: offset += LittleEndian.SHORT_SIZE;
074: _flags = LittleEndian.getShort(tableStream, offset);
075: offset += LittleEndian.SHORT_SIZE;
076: _maxIndex = LittleEndian.getShort(tableStream, offset);
077: offset += LittleEndian.SHORT_SIZE;
078: _maxFixedIndex = LittleEndian.getShort(tableStream, offset);
079: offset += LittleEndian.SHORT_SIZE;
080: _stylenameVersion = LittleEndian.getShort(tableStream, offset);
081: offset += LittleEndian.SHORT_SIZE;
082:
083: _rgftc = new int[3];
084: _rgftc[0] = LittleEndian.getShort(tableStream, offset);
085: offset += LittleEndian.SHORT_SIZE;
086: _rgftc[1] = LittleEndian.getShort(tableStream, offset);
087: offset += LittleEndian.SHORT_SIZE;
088: _rgftc[2] = LittleEndian.getShort(tableStream, offset);
089: offset += LittleEndian.SHORT_SIZE;
090:
091: offset = (LittleEndian.SHORT_SIZE + _stshiLength);
092: _styleDescriptions = new StyleDescription[stdCount];
093: for (int x = 0; x < stdCount; x++) {
094: int stdSize = LittleEndian.getShort(tableStream, offset);
095: //get past the size
096: offset += 2;
097: if (stdSize > 0) {
098: //byte[] std = new byte[stdSize];
099:
100: StyleDescription aStyle = new StyleDescription(
101: tableStream, _baseLength, offset, true);
102:
103: _styleDescriptions[x] = aStyle;
104: }
105:
106: offset += stdSize;
107:
108: }
109: for (int x = 0; x < _styleDescriptions.length; x++) {
110: if (_styleDescriptions[x] != null) {
111: createPap(x);
112: createChp(x);
113: }
114: }
115: }
116:
117: public void writeTo(HWPFOutputStream out) throws IOException {
118: int offset = 0;
119: // add two bytes so we can prepend the stylesheet w/ its size
120: byte[] buf = new byte[_stshiLength + 2];
121: LittleEndian.putShort(buf, offset, (short) _stshiLength);
122: offset += LittleEndian.SHORT_SIZE;
123: LittleEndian.putShort(buf, offset,
124: (short) _styleDescriptions.length);
125: offset += LittleEndian.SHORT_SIZE;
126: LittleEndian.putShort(buf, offset, (short) _baseLength);
127: offset += LittleEndian.SHORT_SIZE;
128: LittleEndian.putShort(buf, offset, (short) _flags);
129: offset += LittleEndian.SHORT_SIZE;
130: LittleEndian.putShort(buf, offset, (short) _maxIndex);
131: offset += LittleEndian.SHORT_SIZE;
132: LittleEndian.putShort(buf, offset, (short) _maxFixedIndex);
133: offset += LittleEndian.SHORT_SIZE;
134: LittleEndian.putShort(buf, offset, (short) _stylenameVersion);
135: offset += LittleEndian.SHORT_SIZE;
136:
137: LittleEndian.putShort(buf, offset, (short) _rgftc[0]);
138: offset += LittleEndian.SHORT_SIZE;
139: LittleEndian.putShort(buf, offset, (short) _rgftc[1]);
140: offset += LittleEndian.SHORT_SIZE;
141: LittleEndian.putShort(buf, offset, (short) _rgftc[2]);
142:
143: out.write(buf);
144:
145: byte[] sizeHolder = new byte[2];
146: for (int x = 0; x < _styleDescriptions.length; x++) {
147: if (_styleDescriptions[x] != null) {
148: byte[] std = _styleDescriptions[x].toByteArray();
149:
150: // adjust the size so it is always on a word boundary
151: LittleEndian.putShort(sizeHolder,
152: (short) ((std.length) + (std.length % 2)));
153: out.write(sizeHolder);
154: out.write(std);
155:
156: // Must always start on a word boundary.
157: if (std.length % 2 == 1) {
158: out.write('\0');
159: }
160: } else {
161: sizeHolder[0] = 0;
162: sizeHolder[1] = 0;
163: out.write(sizeHolder);
164: }
165: }
166: }
167:
168: public boolean equals(Object o) {
169: StyleSheet ss = (StyleSheet) o;
170:
171: if (ss._baseLength == _baseLength && ss._flags == _flags
172: && ss._maxFixedIndex == _maxFixedIndex
173: && ss._maxIndex == _maxIndex
174: && ss._rgftc[0] == _rgftc[0]
175: && ss._rgftc[1] == _rgftc[1]
176: && ss._rgftc[2] == _rgftc[2]
177: && ss._stshiLength == _stshiLength
178: && ss._stylenameVersion == _stylenameVersion) {
179: if (ss._styleDescriptions.length == _styleDescriptions.length) {
180: for (int x = 0; x < _styleDescriptions.length; x++) {
181: // check for null
182: if (ss._styleDescriptions[x] != _styleDescriptions[x]) {
183: // check for equality
184: if (!ss._styleDescriptions[x]
185: .equals(_styleDescriptions[x])) {
186: return false;
187: }
188: }
189: }
190: return true;
191: }
192: }
193: return false;
194: }
195:
196: /**
197: * Creates a PartagraphProperties object from a papx stored in the
198: * StyleDescription at the index istd in the StyleDescription array. The PAP
199: * is placed in the StyleDescription at istd after its been created. Not
200: * every StyleDescription will contain a papx. In these cases this function
201: * does nothing
202: *
203: * @param istd The index of the StyleDescription to create the
204: * ParagraphProperties from (and also place the finished PAP in)
205: */
206: private void createPap(int istd) {
207: StyleDescription sd = _styleDescriptions[istd];
208: ParagraphProperties pap = sd.getPAP();
209: byte[] papx = sd.getPAPX();
210: int baseIndex = sd.getBaseStyle();
211: if (pap == null && papx != null) {
212: ParagraphProperties parentPAP = new ParagraphProperties();
213: if (baseIndex != NIL_STYLE) {
214:
215: parentPAP = _styleDescriptions[baseIndex].getPAP();
216: if (parentPAP == null) {
217: if (baseIndex == istd) {
218: // Oh dear, style claims that it is its own parent
219: throw new IllegalStateException(
220: "Pap style "
221: + istd
222: + " claimed to have itself as its parent, which isn't allowed");
223: } else {
224: // Create the parent style
225: createPap(baseIndex);
226: parentPAP = _styleDescriptions[baseIndex]
227: .getPAP();
228: }
229: }
230:
231: }
232:
233: pap = (ParagraphProperties) ParagraphSprmUncompressor
234: .uncompressPAP(parentPAP, papx, 2);
235: sd.setPAP(pap);
236: }
237: }
238:
239: /**
240: * Creates a CharacterProperties object from a chpx stored in the
241: * StyleDescription at the index istd in the StyleDescription array. The
242: * CharacterProperties object is placed in the StyleDescription at istd after
243: * its been created. Not every StyleDescription will contain a chpx. In these
244: * cases this function does nothing.
245: *
246: * @param istd The index of the StyleDescription to create the
247: * CharacterProperties object from.
248: */
249: private void createChp(int istd) {
250: StyleDescription sd = _styleDescriptions[istd];
251: CharacterProperties chp = sd.getCHP();
252: byte[] chpx = sd.getCHPX();
253: int baseIndex = sd.getBaseStyle();
254: if (chp == null && chpx != null) {
255: CharacterProperties parentCHP = new CharacterProperties();
256: if (baseIndex != NIL_STYLE) {
257:
258: parentCHP = _styleDescriptions[baseIndex].getCHP();
259: if (parentCHP == null) {
260: createChp(baseIndex);
261: parentCHP = _styleDescriptions[baseIndex].getCHP();
262: }
263:
264: }
265:
266: chp = (CharacterProperties) CharacterSprmUncompressor
267: .uncompressCHP(parentCHP, chpx, 0);
268: sd.setCHP(chp);
269: }
270: }
271:
272: /**
273: * Gets the number of styles in the style sheet.
274: * @return The number of styles in the style sheet.
275: */
276: public int numStyles() {
277: return _styleDescriptions.length;
278: }
279:
280: /**
281: * Gets the StyleDescription at index x.
282: *
283: * @param x the index of the desired StyleDescription.
284: */
285: public StyleDescription getStyleDescription(int x) {
286: return _styleDescriptions[x];
287: }
288:
289: public CharacterProperties getCharacterStyle(int x) {
290: if (x == NIL_STYLE) {
291: return NIL_CHP;
292: }
293: return (_styleDescriptions[x] != null ? _styleDescriptions[x]
294: .getCHP() : null);
295: }
296:
297: public ParagraphProperties getParagraphStyle(int x) {
298: if (x == NIL_STYLE) {
299: return NIL_PAP;
300: }
301: return (_styleDescriptions[x] != null ? _styleDescriptions[x]
302: .getPAP() : null);
303: }
304:
305: }
|