| /* ==================================================================== |
| 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.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.apache.poi.hwpf.sprm.SprmBuffer; |
| import org.apache.poi.util.Internal; |
| import org.apache.poi.util.LittleEndian; |
| import org.apache.poi.util.RecordFormatException; |
| |
| /** |
| * Represents a CHP fkp. The style properties for paragraph and character runs |
| * are stored in fkps. There are PAP fkps for paragraph properties and CHP fkps |
| * for character run properties. The first part of the fkp for both CHP and PAP |
| * fkps consists of an array of 4 byte int offsets that represent a |
| * Paragraph's or Character run's text offset in the main stream. The ending |
| * offset is the next value in the array. For example, if an fkp has X number of |
| * Paragraph's stored in it then there are (x + 1) 4 byte ints in the beginning |
| * array. The number X is determined by the last byte in a 512 byte fkp. |
| * |
| * CHP and PAP fkps also store the compressed styles(grpprl) that correspond to |
| * the offsets on the front of the fkp. The offset of the grpprls is determined |
| * differently for CHP fkps and PAP fkps. |
| * |
| * @author Ryan Ackley |
| */ |
| @Internal |
| public final class CHPFormattedDiskPage extends FormattedDiskPage |
| { |
| private static final int FC_SIZE = 4; |
| |
| private ArrayList<CHPX> _chpxList = new ArrayList<CHPX>(); |
| private ArrayList<CHPX> _overFlow; |
| |
| |
| public CHPFormattedDiskPage() |
| { |
| } |
| |
| /** |
| * This constructs a CHPFormattedDiskPage from a raw fkp (512 byte array |
| * read from a Word file). |
| * |
| * @deprecated Use |
| * {@link #CHPFormattedDiskPage(byte[], int, CharIndexTranslator)} |
| * instead |
| */ |
| @Deprecated |
| public CHPFormattedDiskPage( byte[] documentStream, int offset, int fcMin, |
| TextPieceTable tpt ) |
| { |
| this( documentStream, offset, tpt ); |
| } |
| |
| /** |
| * This constructs a CHPFormattedDiskPage from a raw fkp (512 byte array |
| * read from a Word file). |
| */ |
| public CHPFormattedDiskPage( byte[] documentStream, int offset, |
| CharIndexTranslator translator ) |
| { |
| super( documentStream, offset ); |
| |
| for ( int x = 0; x < _crun; x++ ) |
| { |
| int bytesStartAt = getStart( x ); |
| int bytesEndAt = getEnd( x ); |
| |
| // int charStartAt = translator.getCharIndex( bytesStartAt ); |
| // int charEndAt = translator.getCharIndex( bytesEndAt, charStartAt |
| // ); |
| |
| for ( int[] range : translator.getCharIndexRanges( bytesStartAt, |
| bytesEndAt ) ) |
| { |
| CHPX chpx = new CHPX( range[0], range[1], new SprmBuffer( |
| getGrpprl( x ), 0 ) ); |
| _chpxList.add( chpx ); |
| } |
| } |
| } |
| |
| public CHPX getCHPX(int index) |
| { |
| return _chpxList.get(index); |
| } |
| |
| public List<CHPX> getCHPXs() |
| { |
| return Collections.unmodifiableList( _chpxList ); |
| } |
| |
| public void fill(List<CHPX> filler) |
| { |
| _chpxList.addAll(filler); |
| } |
| |
| public ArrayList<CHPX> getOverflow() |
| { |
| return _overFlow; |
| } |
| |
| /** |
| * Gets the chpx for the character run at index in this fkp. |
| * |
| * @param index The index of the chpx to get. |
| * @return a chpx grpprl. |
| */ |
| @Override |
| protected byte[] getGrpprl(int index) |
| { |
| int chpxOffset = 2 * LittleEndian.getUByte(_fkp, _offset + (((_crun + 1) * 4) + index)); |
| |
| //optimization if offset == 0 use "Normal" style |
| if(chpxOffset == 0) |
| { |
| return new byte[0]; |
| } |
| |
| int size = LittleEndian.getUByte(_fkp, _offset + chpxOffset); |
| |
| byte[] chpx = new byte[size]; |
| |
| System.arraycopy(_fkp, _offset + ++chpxOffset, chpx, 0, size); |
| return chpx; |
| } |
| |
| /** |
| * @deprecated Use {@link #toByteArray(CharIndexTranslator)} instead |
| */ |
| @Deprecated |
| protected byte[] toByteArray(CharIndexTranslator translator, int fcMin) |
| { |
| return toByteArray( translator ); |
| } |
| |
| protected byte[] toByteArray( CharIndexTranslator translator ) |
| { |
| byte[] buf = new byte[512]; |
| int size = _chpxList.size(); |
| int grpprlOffset = 511; |
| int offsetOffset = 0; |
| int fcOffset = 0; |
| |
| // total size is currently the size of one FC |
| int totalSize = FC_SIZE + 2; |
| |
| int index = 0; |
| for ( ; index < size; index++ ) |
| { |
| int grpprlLength = ( _chpxList.get( index ) ).getGrpprl().length; |
| |
| // check to see if we have enough room for an FC, the grpprl offset, |
| // the grpprl size byte and the grpprl. |
| totalSize += ( FC_SIZE + 2 + grpprlLength ); |
| // if size is uneven we will have to add one so the first grpprl |
| // falls on a word boundary |
| if ( totalSize > 511 + ( index % 2 ) ) |
| { |
| totalSize -= ( FC_SIZE + 2 + grpprlLength ); |
| break; |
| } |
| |
| // grpprls must fall on word boundaries |
| if ( ( 1 + grpprlLength ) % 2 > 0 ) |
| { |
| totalSize += 1; |
| } |
| } |
| |
| if (index == 0) { |
| throw new RecordFormatException("empty grpprl entry."); |
| } |
| |
| // see if we couldn't fit some |
| if ( index != size ) |
| { |
| _overFlow = new ArrayList<CHPX>(); |
| _overFlow.addAll( _chpxList.subList( index, size ) ); |
| } |
| |
| // index should equal number of CHPXs that will be in this fkp now. |
| buf[511] = (byte) index; |
| |
| offsetOffset = ( FC_SIZE * index ) + FC_SIZE; |
| // grpprlOffset = offsetOffset + index + (grpprlOffset % 2); |
| |
| int chpxEnd = 0; |
| for ( CHPX chpx : _chpxList.subList(0, index)) { |
| int chpxStart = translator.getByteIndex( chpx.getStart() ); |
| chpxEnd = translator.getByteIndex( chpx.getEnd() ); |
| LittleEndian.putInt( buf, fcOffset, chpxStart ); |
| |
| byte[] grpprl = chpx.getGrpprl(); |
| grpprlOffset -= ( 1 + grpprl.length ); |
| grpprlOffset -= ( grpprlOffset % 2 ); |
| buf[offsetOffset] = (byte) ( grpprlOffset / 2 ); |
| buf[grpprlOffset] = (byte) grpprl.length; |
| System.arraycopy( grpprl, 0, buf, grpprlOffset + 1, grpprl.length ); |
| |
| offsetOffset += 1; |
| fcOffset += FC_SIZE; |
| } |
| // put the last chpx's end in |
| LittleEndian.putInt( buf, fcOffset, chpxEnd ); |
| return buf; |
| } |
| |
| } |