123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- /* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You 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.hwpf.model;
-
- import java.io.IOException;
- import java.io.OutputStream;
-
- import org.apache.poi.hwpf.sprm.CharacterSprmUncompressor;
- import org.apache.poi.hwpf.sprm.ParagraphSprmUncompressor;
- import org.apache.poi.hwpf.usermodel.CharacterProperties;
- import org.apache.poi.hwpf.usermodel.ParagraphProperties;
- import org.apache.poi.util.Internal;
- import org.apache.poi.util.LittleEndian;
- import org.apache.poi.util.LittleEndianConsts;
-
- /**
- * Represents a document's stylesheet. A word documents formatting is stored as
- * compressed styles that are based on styles contained in the stylesheet. This
- * class also contains static utility functions to uncompress different
- * formatting properties.
- * <p>
- * Fields documentation is quotes from Microsoft Office Word 97-2007 Binary File
- * Format (.doc) Specification, page 36 of 210
- *
- * @author Ryan Ackley
- */
- @Internal
- public final class StyleSheet {
-
- public static final int NIL_STYLE = 4095;
- // private static final int PAP_TYPE = 1;
- // private static final int CHP_TYPE = 2;
- // private static final int SEP_TYPE = 4;
- // private static final int TAP_TYPE = 5;
-
- @Deprecated
- private final static ParagraphProperties NIL_PAP = new ParagraphProperties();
- @Deprecated
- private final static CharacterProperties NIL_CHP = new CharacterProperties();
-
- private final static byte[] NIL_CHPX = new byte[]{};
- private final static byte[] NIL_PAPX = new byte[]{0, 0};
-
- /**
- * Size of the STSHI structure
- */
- private int _cbStshi;
-
- /**
- * General information about a stylesheet
- */
- private Stshif _stshif;
-
- StyleDescription[] _styleDescriptions;
-
- /**
- * StyleSheet constructor. Loads a document's stylesheet information,
- *
- * @param tableStream A byte array containing a document's raw stylesheet
- * info. Found by using FileInformationBlock.getFcStshf() and
- * FileInformationBLock.getLcbStshf()
- */
- public StyleSheet(byte[] tableStream, int offset) {
- int startOffset = offset;
- _cbStshi = LittleEndian.getShort(tableStream, offset);
- offset += LittleEndianConsts.SHORT_SIZE;
-
- /*
- * Count of styles in stylesheet
- *
- * The number of styles in this style sheet. There will be stshi.cstd
- * (cbSTD, STD) pairs in the file following the STSHI. Note: styles can
- * be empty, i.e. cbSTD==0.
- */
-
- _stshif = new Stshif(tableStream, offset);
-
- // shall we discard cbLSD and mpstilsd?
-
- offset = startOffset + LittleEndianConsts.SHORT_SIZE + _cbStshi;
- _styleDescriptions = new StyleDescription[_stshif.getCstd()];
- for (int x = 0; x < _stshif.getCstd(); x++) {
- int stdSize = LittleEndian.getShort(tableStream, offset);
- //get past the size
- offset += 2;
- if (stdSize > 0) {
- //byte[] std = new byte[stdSize];
-
- StyleDescription aStyle = new StyleDescription(tableStream,
- _stshif.getCbSTDBaseInFile(), offset, true);
-
- _styleDescriptions[x] = aStyle;
- }
-
- offset += stdSize;
-
- }
- for (int x = 0; x < _styleDescriptions.length; x++) {
- if (_styleDescriptions[x] != null) {
- createPap(x);
- createChp(x);
- }
- }
- }
-
- public void writeTo(OutputStream out)
- throws IOException {
-
- int offset = 0;
-
- /*
- * we don't support 2003 Word extensions in STSHI (but may be we should
- * at least not delete them, shouldn't we?), so our structure is always
- * 18 bytes in length -- sergey
- */
- this._cbStshi = 18;
-
- // add two bytes so we can prepend the stylesheet w/ its size
- byte[] buf = new byte[_cbStshi + 2];
-
- LittleEndian.putUShort(buf, offset, (short) _cbStshi);
- offset += LittleEndianConsts.SHORT_SIZE;
-
- _stshif.setCstd(_styleDescriptions.length);
- _stshif.serialize(buf, offset);
- // offset += Stshif.getSize();
-
- out.write(buf);
-
- byte[] sizeHolder = new byte[2];
- for (StyleDescription styleDescription : _styleDescriptions) {
- if (styleDescription != null) {
- byte[] std = styleDescription.toByteArray();
-
- // adjust the size so it is always on a word boundary
- LittleEndian.putShort(sizeHolder, 0, (short) ((std.length) + (std.length % 2)));
- out.write(sizeHolder);
- out.write(std);
-
- // Must always start on a word boundary.
- if (std.length % 2 == 1) {
- out.write('\0');
- }
- } else {
- sizeHolder[0] = 0;
- sizeHolder[1] = 0;
- out.write(sizeHolder);
- }
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof StyleSheet)) return false;
- StyleSheet ss = (StyleSheet) o;
-
- if (!ss._stshif.equals(this._stshif)
- || ss._cbStshi != this._cbStshi
- || ss._styleDescriptions.length != this._styleDescriptions.length
- ) return false;
-
- for (int i = 0; i < _styleDescriptions.length; i++) {
- StyleDescription tsd = this._styleDescriptions[i];
- StyleDescription osd = ss._styleDescriptions[i];
- if (tsd == null && osd == null) continue;
- if (osd == null || !osd.equals(tsd)) return false;
- }
-
- return true;
- }
-
- @Override
- public int hashCode() {
- assert false : "hashCode not designed";
- return 42; // any arbitrary constant will do
- }
-
- /**
- * Creates a PartagraphProperties object from a papx stored in the
- * StyleDescription at the index istd in the StyleDescription array. The PAP
- * is placed in the StyleDescription at istd after its been created. Not
- * every StyleDescription will contain a papx. In these cases this function
- * does nothing
- *
- * @param istd The index of the StyleDescription to create the
- * ParagraphProperties from (and also place the finished PAP in)
- */
- @Deprecated
- private void createPap(int istd) {
- StyleDescription sd = _styleDescriptions[istd];
- ParagraphProperties pap = sd.getPAP();
- byte[] papx = sd.getPAPX();
- int baseIndex = sd.getBaseStyle();
- if (pap == null && papx != null) {
- ParagraphProperties parentPAP = new ParagraphProperties();
- if (baseIndex != NIL_STYLE) {
-
- parentPAP = _styleDescriptions[baseIndex].getPAP();
- if (parentPAP == null) {
- if (baseIndex == istd) {
- // Oh dear, style claims that it is its own parent
- throw new IllegalStateException("Pap style " + istd + " claimed to have itself as its parent, which isn't allowed");
- }
- // Create the parent style
- createPap(baseIndex);
- parentPAP = _styleDescriptions[baseIndex].getPAP();
- }
-
- }
-
- if (parentPAP == null) {
- parentPAP = new ParagraphProperties();
- }
-
- pap = ParagraphSprmUncompressor.uncompressPAP(parentPAP, papx, 2);
- sd.setPAP(pap);
- }
- }
-
- /**
- * Creates a CharacterProperties object from a chpx stored in the
- * StyleDescription at the index istd in the StyleDescription array. The
- * CharacterProperties object is placed in the StyleDescription at istd after
- * its been created. Not every StyleDescription will contain a chpx. In these
- * cases this function does nothing.
- *
- * @param istd The index of the StyleDescription to create the
- * CharacterProperties object from.
- */
- @Deprecated
- private void createChp(int istd) {
- StyleDescription sd = _styleDescriptions[istd];
- CharacterProperties chp = sd.getCHP();
- byte[] chpx = sd.getCHPX();
- int baseIndex = sd.getBaseStyle();
-
- if (baseIndex == istd) {
- // Oh dear, this isn't allowed...
- // The word file seems to be corrupted
- // Switch to using the nil style so that
- // there's a chance we can read it
- baseIndex = NIL_STYLE;
- }
-
- // Build and decompress the Chp if required
- if (chp == null && chpx != null) {
- CharacterProperties parentCHP = new CharacterProperties();
- if (baseIndex != NIL_STYLE) {
- parentCHP = _styleDescriptions[baseIndex].getCHP();
- if (parentCHP == null) {
- createChp(baseIndex);
- parentCHP = _styleDescriptions[baseIndex].getCHP();
- }
- if (parentCHP == null) {
- parentCHP = new CharacterProperties();
- }
- }
-
- chp = CharacterSprmUncompressor.uncompressCHP(parentCHP, chpx, 0);
- sd.setCHP(chp);
- }
- }
-
- /**
- * Gets the number of styles in the style sheet.
- *
- * @return The number of styles in the style sheet.
- */
- public int numStyles() {
- return _styleDescriptions.length;
- }
-
- /**
- * Gets the StyleDescription at index x.
- *
- * @param styleIndex the index of the desired StyleDescription.
- */
- public StyleDescription getStyleDescription(int styleIndex) {
- return _styleDescriptions[styleIndex];
- }
-
- @Deprecated
- public CharacterProperties getCharacterStyle(int styleIndex) {
- if (styleIndex == NIL_STYLE) {
- return NIL_CHP;
- }
-
- if (styleIndex >= _styleDescriptions.length) {
- return NIL_CHP;
- }
-
- if (styleIndex == -1) {
- return NIL_CHP;
- }
-
- return (_styleDescriptions[styleIndex] != null ? _styleDescriptions[styleIndex]
- .getCHP() : NIL_CHP);
- }
-
- @Deprecated
- public ParagraphProperties getParagraphStyle(int styleIndex) {
- if (styleIndex == NIL_STYLE) {
- return NIL_PAP;
- }
-
- if (styleIndex >= _styleDescriptions.length) {
- return NIL_PAP;
- }
-
- if (styleIndex == -1) {
- return NIL_PAP;
- }
-
- if (_styleDescriptions[styleIndex] == null) {
- return NIL_PAP;
- }
-
- if (_styleDescriptions[styleIndex].getPAP() == null) {
- return NIL_PAP;
- }
-
- return _styleDescriptions[styleIndex].getPAP();
- }
-
- public byte[] getCHPX(int styleIndex) {
- if (styleIndex == NIL_STYLE) {
- return NIL_CHPX;
- }
-
- if (styleIndex >= _styleDescriptions.length) {
- return NIL_CHPX;
- }
-
- if (styleIndex == -1) {
- return NIL_CHPX;
- }
-
- if (_styleDescriptions[styleIndex] == null) {
- return NIL_CHPX;
- }
-
- if (_styleDescriptions[styleIndex].getCHPX() == null) {
- return NIL_CHPX;
- }
-
- return _styleDescriptions[styleIndex].getCHPX();
- }
-
- public byte[] getPAPX(int styleIndex) {
- if (styleIndex == NIL_STYLE) {
- return NIL_PAPX;
- }
-
- if (styleIndex >= _styleDescriptions.length) {
- return NIL_PAPX;
- }
-
- if (styleIndex == -1) {
- return NIL_PAPX;
- }
-
- if (_styleDescriptions[styleIndex] == null) {
- return NIL_PAPX;
- }
-
- if (_styleDescriptions[styleIndex].getPAPX() == null) {
- return NIL_PAPX;
- }
-
- return _styleDescriptions[styleIndex].getPAPX();
- }
- }
|