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.List;
021: import java.util.ArrayList;
022:
023: import org.apache.poi.util.LittleEndian;
024:
025: /**
026: * Represents a CHP fkp. The style properties for paragraph and character runs
027: * are stored in fkps. There are PAP fkps for paragraph properties and CHP fkps
028: * for character run properties. The first part of the fkp for both CHP and PAP
029: * fkps consists of an array of 4 byte int offsets that represent a
030: * Paragraph's or Character run's text offset in the main stream. The ending
031: * offset is the next value in the array. For example, if an fkp has X number of
032: * Paragraph's stored in it then there are (x + 1) 4 byte ints in the beginning
033: * array. The number X is determined by the last byte in a 512 byte fkp.
034: *
035: * CHP and PAP fkps also store the compressed styles(grpprl) that correspond to
036: * the offsets on the front of the fkp. The offset of the grpprls is determined
037: * differently for CHP fkps and PAP fkps.
038: *
039: * @author Ryan Ackley
040: */
041: public class CHPFormattedDiskPage extends FormattedDiskPage {
042: private static final int FC_SIZE = 4;
043:
044: private ArrayList _chpxList = new ArrayList();
045: private ArrayList _overFlow;
046:
047: public CHPFormattedDiskPage() {
048: }
049:
050: /**
051: * This constructs a CHPFormattedDiskPage from a raw fkp (512 byte array
052: * read from a Word file).
053: */
054: public CHPFormattedDiskPage(byte[] documentStream, int offset,
055: int fcMin) {
056: super (documentStream, offset);
057:
058: for (int x = 0; x < _crun; x++) {
059: _chpxList.add(new CHPX(getStart(x) - fcMin, getEnd(x)
060: - fcMin, getGrpprl(x)));
061: }
062: }
063:
064: public CHPX getCHPX(int index) {
065: return (CHPX) _chpxList.get(index);
066: }
067:
068: public void fill(List filler) {
069: _chpxList.addAll(filler);
070: }
071:
072: public ArrayList getOverflow() {
073: return _overFlow;
074: }
075:
076: /**
077: * Gets the chpx for the character run at index in this fkp.
078: *
079: * @param index The index of the chpx to get.
080: * @return a chpx grpprl.
081: */
082: protected byte[] getGrpprl(int index) {
083: int chpxOffset = 2 * LittleEndian.getUnsignedByte(_fkp, _offset
084: + (((_crun + 1) * 4) + index));
085:
086: //optimization if offset == 0 use "Normal" style
087: if (chpxOffset == 0) {
088: return new byte[0];
089: }
090:
091: int size = LittleEndian.getUnsignedByte(_fkp, _offset
092: + chpxOffset);
093:
094: byte[] chpx = new byte[size];
095:
096: System.arraycopy(_fkp, _offset + ++chpxOffset, chpx, 0, size);
097: return chpx;
098: }
099:
100: protected byte[] toByteArray(int fcMin) {
101: byte[] buf = new byte[512];
102: int size = _chpxList.size();
103: int grpprlOffset = 511;
104: int offsetOffset = 0;
105: int fcOffset = 0;
106:
107: // total size is currently the size of one FC
108: int totalSize = FC_SIZE + 2;
109:
110: int index = 0;
111: for (; index < size; index++) {
112: int grpprlLength = ((CHPX) _chpxList.get(index))
113: .getGrpprl().length;
114:
115: // check to see if we have enough room for an FC, the grpprl offset,
116: // the grpprl size byte and the grpprl.
117: totalSize += (FC_SIZE + 2 + grpprlLength);
118: // if size is uneven we will have to add one so the first grpprl falls
119: // on a word boundary
120: if (totalSize > 511 + (index % 2)) {
121: totalSize -= (FC_SIZE + 2 + grpprlLength);
122: break;
123: }
124:
125: // grpprls must fall on word boundaries
126: if ((1 + grpprlLength) % 2 > 0) {
127: totalSize += 1;
128: }
129: }
130:
131: // see if we couldn't fit some
132: if (index != size) {
133: _overFlow = new ArrayList();
134: _overFlow.addAll(_chpxList.subList(index, size));
135: }
136:
137: // index should equal number of CHPXs that will be in this fkp now.
138: buf[511] = (byte) index;
139:
140: offsetOffset = (FC_SIZE * index) + FC_SIZE;
141: //grpprlOffset = offsetOffset + index + (grpprlOffset % 2);
142:
143: CHPX chpx = null;
144: for (int x = 0; x < index; x++) {
145: chpx = (CHPX) _chpxList.get(x);
146: byte[] grpprl = chpx.getGrpprl();
147:
148: LittleEndian.putInt(buf, fcOffset, chpx.getStart() + fcMin);
149: grpprlOffset -= (1 + grpprl.length);
150: grpprlOffset -= (grpprlOffset % 2);
151: buf[offsetOffset] = (byte) (grpprlOffset / 2);
152: buf[grpprlOffset] = (byte) grpprl.length;
153: System.arraycopy(grpprl, 0, buf, grpprlOffset + 1,
154: grpprl.length);
155:
156: offsetOffset += 1;
157: fcOffset += FC_SIZE;
158: }
159: // put the last chpx's end in
160: LittleEndian.putInt(buf, fcOffset, chpx.getEnd() + fcMin);
161: return buf;
162: }
163:
164: }
|