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.model.textproperties;
019:
020: import java.io.IOException;
021: import java.io.OutputStream;
022: import java.util.LinkedList;
023:
024: import org.apache.poi.hslf.record.StyleTextPropAtom;
025: import org.apache.poi.util.LittleEndian;
026:
027: /**
028: * For a given run of characters, holds the properties (which could
029: * be paragraph properties or character properties).
030: * Used to hold the number of characters affected, the list of active
031: * properties, and the random reserved field if required.
032: */
033: public class TextPropCollection {
034: private int charactersCovered;
035: private short reservedField;
036: private LinkedList textPropList;
037:
038: /** Fetch the number of characters this styling applies to */
039: public int getCharactersCovered() {
040: return charactersCovered;
041: }
042:
043: /** Fetch the TextProps that define this styling */
044: public LinkedList getTextPropList() {
045: return textPropList;
046: }
047:
048: /** Fetch the TextProp with this name, or null if it isn't present */
049: public TextProp findByName(String textPropName) {
050: for (int i = 0; i < textPropList.size(); i++) {
051: TextProp prop = (TextProp) textPropList.get(i);
052: if (prop.getName().equals(textPropName)) {
053: return prop;
054: }
055: }
056: return null;
057: }
058:
059: /** Add the TextProp with this name to the list */
060: public TextProp addWithName(String name) {
061: // Find the base TextProp to base on
062: TextProp base = null;
063: for (int i = 0; i < StyleTextPropAtom.characterTextPropTypes.length; i++) {
064: if (StyleTextPropAtom.characterTextPropTypes[i].getName()
065: .equals(name)) {
066: base = StyleTextPropAtom.characterTextPropTypes[i];
067: }
068: }
069: for (int i = 0; i < StyleTextPropAtom.paragraphTextPropTypes.length; i++) {
070: if (StyleTextPropAtom.paragraphTextPropTypes[i].getName()
071: .equals(name)) {
072: base = StyleTextPropAtom.paragraphTextPropTypes[i];
073: }
074: }
075: if (base == null) {
076: throw new IllegalArgumentException("No TextProp with name "
077: + name + " is defined to add from");
078: }
079:
080: // Add a copy of this property, in the right place to the list
081: TextProp textProp = (TextProp) base.clone();
082: int pos = 0;
083: for (int i = 0; i < textPropList.size(); i++) {
084: TextProp curProp = (TextProp) textPropList.get(i);
085: if (textProp.getMask() > curProp.getMask()) {
086: pos++;
087: }
088: }
089: textPropList.add(pos, textProp);
090: return textProp;
091: }
092:
093: /**
094: * For an existing set of text properties, build the list of
095: * properties coded for in a given run of properties.
096: * @return the number of bytes that were used encoding the properties list
097: */
098: public int buildTextPropList(int containsField,
099: TextProp[] potentialProperties, byte[] data, int dataOffset) {
100: int bytesPassed = 0;
101:
102: // For each possible entry, see if we match the mask
103: // If we do, decode that, save it, and shuffle on
104: for (int i = 0; i < potentialProperties.length; i++) {
105: // Check there's still data left to read
106: if (dataOffset + bytesPassed >= data.length) {
107: // Out of data, can't be any more properties to go
108: return bytesPassed;
109: }
110:
111: // Check if this property is found in the mask
112: if ((containsField & potentialProperties[i].getMask()) != 0) {
113: // Bingo, data contains this property
114: TextProp prop = (TextProp) potentialProperties[i]
115: .clone();
116: int val = 0;
117: if (prop.getSize() == 2) {
118: val = LittleEndian.getShort(data, dataOffset
119: + bytesPassed);
120: } else {
121: val = LittleEndian.getInt(data, dataOffset
122: + bytesPassed);
123: }
124: prop.setValue(val);
125: bytesPassed += prop.getSize();
126: textPropList.add(prop);
127: }
128: }
129:
130: // Return how many bytes were used
131: return bytesPassed;
132: }
133:
134: /**
135: * Create a new collection of text properties (be they paragraph
136: * or character) which will be groked via a subsequent call to
137: * buildTextPropList().
138: */
139: public TextPropCollection(int charactersCovered, short reservedField) {
140: this .charactersCovered = charactersCovered;
141: this .reservedField = reservedField;
142: textPropList = new LinkedList();
143: }
144:
145: /**
146: * Create a new collection of text properties (be they paragraph
147: * or character) for a run of text without any
148: */
149: public TextPropCollection(int textSize) {
150: charactersCovered = textSize;
151: reservedField = -1;
152: textPropList = new LinkedList();
153: }
154:
155: /**
156: * Update the size of the text that this set of properties
157: * applies to
158: */
159: public void updateTextSize(int textSize) {
160: charactersCovered = textSize;
161: }
162:
163: /**
164: * Writes out to disk the header, and then all the properties
165: */
166: public void writeOut(OutputStream o) throws IOException {
167: // First goes the number of characters we affect
168: StyleTextPropAtom.writeLittleEndian(charactersCovered, o);
169:
170: // Then we have the reserved field if required
171: if (reservedField > -1) {
172: StyleTextPropAtom.writeLittleEndian(reservedField, o);
173: }
174:
175: // Then the mask field
176: int mask = 0;
177: for (int i = 0; i < textPropList.size(); i++) {
178: TextProp textProp = (TextProp) textPropList.get(i);
179: //sometimes header indicates that the bitmask is present but its value is 0
180: if (textProp instanceof BitMaskTextProp)
181: mask |= (textProp.getWriteMask() == 0 ? 1 : textProp
182: .getWriteMask());
183: else
184: mask |= textProp.getWriteMask();
185: }
186: StyleTextPropAtom.writeLittleEndian(mask, o);
187:
188: // Then the contents of all the properties
189: for (int i = 0; i < textPropList.size(); i++) {
190: TextProp textProp = (TextProp) textPropList.get(i);
191: int val = textProp.getValue();
192: if (textProp.getSize() == 2) {
193: StyleTextPropAtom.writeLittleEndian((short) val, o);
194: } else {
195: StyleTextPropAtom.writeLittleEndian(val, o);
196: }
197: }
198: }
199:
200: public short getReservedField() {
201: return reservedField;
202: }
203:
204: public void setReservedField(short val) {
205: reservedField = val;
206: }
207: }
|