refactor list format override structures (was marked with @Internal annotation)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1389037 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
edae5cb060
commit
9f666b8aee
@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.poi.util;
|
||||
|
||||
|
||||
/**
|
||||
* Utility classes for dealing with arrays.
|
||||
*
|
||||
@ -129,6 +130,28 @@ public class ArrayUtil
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the specified array into specified result array, truncating or
|
||||
* padding with zeros (if necessary) so the copy has the specified length.
|
||||
* This method is temporary replace for Arrays.copyOf() until we start to
|
||||
* require JDK 1.6.
|
||||
*
|
||||
* @param source
|
||||
* the array to be copied
|
||||
* @param result
|
||||
* the array to be filled and returned
|
||||
* @throws NegativeArraySizeException
|
||||
* if <tt>newLength</tt> is negative
|
||||
* @throws NullPointerException
|
||||
* if <tt>original</tt> is null
|
||||
*/
|
||||
public static <T> T[] copyOf( T[] source, T[] result )
|
||||
{
|
||||
System.arraycopy( source, 0, result, 0,
|
||||
Math.min( source.length, result.length ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the specified range of the specified array into a new array.
|
||||
* The initial index of the range (<tt>from</tt>) must lie between zero
|
||||
|
@ -30,13 +30,13 @@ import java.util.regex.Pattern;
|
||||
import org.apache.poi.hpsf.SummaryInformation;
|
||||
import org.apache.poi.hwpf.HWPFDocument;
|
||||
import org.apache.poi.hwpf.HWPFDocumentCore;
|
||||
import org.apache.poi.hwpf.converter.AbstractWordUtils.NumberingState;
|
||||
import org.apache.poi.hwpf.converter.FontReplacer.Triplet;
|
||||
import org.apache.poi.hwpf.model.FieldsDocumentPart;
|
||||
import org.apache.poi.hwpf.model.ListFormatOverride;
|
||||
import org.apache.poi.hwpf.model.ListTables;
|
||||
import org.apache.poi.hwpf.usermodel.Bookmark;
|
||||
import org.apache.poi.hwpf.usermodel.CharacterRun;
|
||||
import org.apache.poi.hwpf.usermodel.Field;
|
||||
import org.apache.poi.hwpf.usermodel.HWPFList;
|
||||
import org.apache.poi.hwpf.usermodel.Notes;
|
||||
import org.apache.poi.hwpf.usermodel.OfficeDrawing;
|
||||
import org.apache.poi.hwpf.usermodel.Paragraph;
|
||||
@ -172,6 +172,8 @@ public abstract class AbstractWordConverter
|
||||
|
||||
private FontReplacer fontReplacer = new DefaultFontReplacer();
|
||||
|
||||
private NumberingState numberingState = new NumberingState();
|
||||
|
||||
private PicturesManager picturesManager;
|
||||
|
||||
/**
|
||||
@ -1022,9 +1024,6 @@ public abstract class AbstractWordConverter
|
||||
protected void processParagraphes( HWPFDocumentCore wordDocument,
|
||||
Element flow, Range range, int currentTableLevel )
|
||||
{
|
||||
final ListTables listTables = wordDocument.getListTables();
|
||||
int currentListInfo = 0;
|
||||
|
||||
final int paragraphs = range.numParagraphs();
|
||||
for ( int p = 0; p < paragraphs; p++ )
|
||||
{
|
||||
@ -1054,38 +1053,17 @@ public abstract class AbstractWordConverter
|
||||
processPageBreak( wordDocument, flow );
|
||||
}
|
||||
|
||||
if ( paragraph.getIlfo() != currentListInfo )
|
||||
if ( paragraph.isInList() )
|
||||
{
|
||||
currentListInfo = paragraph.getIlfo();
|
||||
}
|
||||
HWPFList hwpfList = paragraph.getList();
|
||||
|
||||
if ( currentListInfo != 0 )
|
||||
{
|
||||
if ( listTables != null )
|
||||
{
|
||||
final ListFormatOverride listFormatOverride = listTables
|
||||
.getOverride( paragraph.getIlfo() );
|
||||
|
||||
String label = AbstractWordUtils.getBulletText( listTables,
|
||||
paragraph, listFormatOverride.getLsid() );
|
||||
String label = AbstractWordUtils.getBulletText( numberingState,
|
||||
hwpfList, (char) paragraph.getIlvl() );
|
||||
|
||||
processParagraph( wordDocument, flow, currentTableLevel,
|
||||
paragraph, label );
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.log( POILogger.WARN,
|
||||
"Paragraph #" + paragraph.getStartOffset() + "-"
|
||||
+ paragraph.getEndOffset()
|
||||
+ " has reference to list structure #"
|
||||
+ currentListInfo
|
||||
+ ", but listTables not defined in file" );
|
||||
|
||||
processParagraph( wordDocument, flow, currentTableLevel,
|
||||
paragraph, AbstractWordUtils.EMPTY );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
processParagraph( wordDocument, flow, currentTableLevel,
|
||||
paragraph, AbstractWordUtils.EMPTY );
|
||||
|
@ -41,7 +41,8 @@ import org.apache.poi.hwpf.model.CHPX;
|
||||
import org.apache.poi.hwpf.model.FieldsDocumentPart;
|
||||
import org.apache.poi.hwpf.model.FileInformationBlock;
|
||||
import org.apache.poi.hwpf.model.GenericPropertyNode;
|
||||
import org.apache.poi.hwpf.model.ListFormatOverride;
|
||||
import org.apache.poi.hwpf.model.LFO;
|
||||
import org.apache.poi.hwpf.model.LFOData;
|
||||
import org.apache.poi.hwpf.model.ListLevel;
|
||||
import org.apache.poi.hwpf.model.ListTables;
|
||||
import org.apache.poi.hwpf.model.PAPFormattedDiskPage;
|
||||
@ -716,30 +717,36 @@ public final class HWPFLister
|
||||
{
|
||||
if ( paragraph.getIlfo() != 0 )
|
||||
{
|
||||
final ListFormatOverride listFormatOverride = listTables
|
||||
.getOverride( paragraph.getIlfo() );
|
||||
final LFO lfo = listTables.getLfo( paragraph.getIlfo() );
|
||||
System.out.println( "PAP's LFO: " + lfo );
|
||||
|
||||
System.out.println( "PAP's LFO: " + listFormatOverride );
|
||||
final LFOData lfoData = listTables.getLfoData( paragraph.getIlfo() );
|
||||
System.out.println( "PAP's LFOData: " + lfoData );
|
||||
|
||||
final ListLevel listLevel = listTables.getLevel(
|
||||
listFormatOverride.getLsid(), paragraph.getIlvl() );
|
||||
if ( lfo != null )
|
||||
{
|
||||
final ListLevel listLevel = listTables.getLevel( lfo.getLsid(),
|
||||
paragraph.getIlvl() );
|
||||
|
||||
System.out.println( "PAP's ListLevel: " + listLevel );
|
||||
if ( listLevel.getGrpprlPapx() != null )
|
||||
{
|
||||
System.out.println( "PAP's ListLevel PAPX:" );
|
||||
dumpSprms( new SprmIterator( listLevel.getGrpprlPapx(), 0 ),
|
||||
dumpSprms(
|
||||
new SprmIterator( listLevel.getGrpprlPapx(), 0 ),
|
||||
"* " );
|
||||
}
|
||||
|
||||
if ( listLevel.getGrpprlPapx() != null )
|
||||
{
|
||||
System.out.println( "PAP's ListLevel CHPX:" );
|
||||
dumpSprms( new SprmIterator( listLevel.getGrpprlChpx(), 0 ),
|
||||
dumpSprms(
|
||||
new SprmIterator( listLevel.getGrpprlChpx(), 0 ),
|
||||
"* " );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpTextPieces( boolean withText )
|
||||
{
|
||||
|
@ -31,12 +31,18 @@ import org.apache.poi.util.LittleEndian;
|
||||
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
|
||||
*/
|
||||
@Internal
|
||||
class LFOData
|
||||
public class LFOData
|
||||
{
|
||||
private int _cp;
|
||||
|
||||
private ListFormatOverrideLevel[] _rgLfoLvl;
|
||||
|
||||
public LFOData()
|
||||
{
|
||||
_cp = 0;
|
||||
_rgLfoLvl = new ListFormatOverrideLevel[0];
|
||||
}
|
||||
|
||||
LFOData( byte[] buf, int startOffset, int cLfolvl )
|
||||
{
|
||||
int offset = startOffset;
|
||||
@ -52,17 +58,17 @@ class LFOData
|
||||
}
|
||||
}
|
||||
|
||||
int getCp()
|
||||
public int getCp()
|
||||
{
|
||||
return _cp;
|
||||
}
|
||||
|
||||
ListFormatOverrideLevel[] getRgLfoLvl()
|
||||
public ListFormatOverrideLevel[] getRgLfoLvl()
|
||||
{
|
||||
return _rgLfoLvl;
|
||||
}
|
||||
|
||||
int getSizeInBytes()
|
||||
public int getSizeInBytes()
|
||||
{
|
||||
int result = 0;
|
||||
result += LittleEndian.INT_SIZE;
|
||||
|
@ -19,15 +19,8 @@ package org.apache.poi.hwpf.model;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
|
||||
import org.apache.poi.util.Internal;
|
||||
@ -43,8 +36,11 @@ public final class ListTables
|
||||
{
|
||||
private static POILogger log = POILogFactory.getLogger(ListTables.class);
|
||||
|
||||
ListMap _listMap = new ListMap();
|
||||
ArrayList<ListFormatOverride> _overrideList = new ArrayList<ListFormatOverride>();
|
||||
/**
|
||||
* Both PlfLst and the following LVLs
|
||||
*/
|
||||
private final LinkedHashMap<Integer, ListData> _listMap = new LinkedHashMap<Integer, ListData>();
|
||||
private PlfLfo _plfLfo;
|
||||
|
||||
public ListTables()
|
||||
{
|
||||
@ -52,13 +48,13 @@ public final class ListTables
|
||||
}
|
||||
|
||||
public ListTables( byte[] tableStream, final int lstOffset,
|
||||
final int lfoOffset )
|
||||
{
|
||||
final int fcPlfLfo, final int lcbPlfLfo )
|
||||
{
|
||||
|
||||
/*
|
||||
* The PlfLst structure contains the list formatting information for
|
||||
* the document. -- Page 425 of 621. [MS-DOC] -- v20110315 Word
|
||||
* (.doc) Binary File Format
|
||||
* The PlfLst structure contains the list formatting information for the
|
||||
* document. -- Page 425 of 621. [MS-DOC] -- v20110315 Word (.doc)
|
||||
* Binary File Format
|
||||
*/
|
||||
int offset = lstOffset;
|
||||
|
||||
@ -80,72 +76,15 @@ public final class ListTables
|
||||
lst.setLevel( y, lvl );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/*
|
||||
* The PlfLfo structure contains the list format override data for
|
||||
* the document. -- Page 424 of 621. [MS-DOC] -- v20110315 Word
|
||||
* (.doc) Binary File Format
|
||||
*/
|
||||
int offset = lfoOffset;
|
||||
|
||||
/*
|
||||
* lfoMac (4 bytes): An unsigned integer that specifies the count of
|
||||
* elements in both the rgLfo and rgLfoData arrays. -- Page 424 of
|
||||
* 621. [MS-DOC] -- v20110315 Word (.doc) Binary File Format
|
||||
*/
|
||||
long lfoMac = LittleEndian.getUInt( tableStream, offset );
|
||||
offset += LittleEndian.INT_SIZE;
|
||||
|
||||
/*
|
||||
* An array of LFO structures. The number of elements in this array
|
||||
* is specified by lfoMac. -- Page 424 of 621. [MS-DOC] -- v20110315
|
||||
* Word (.doc) Binary File Format
|
||||
*/
|
||||
for ( int x = 0; x < lfoMac; x++ )
|
||||
{
|
||||
ListFormatOverride lfo = new ListFormatOverride( tableStream,
|
||||
offset );
|
||||
offset += LFO.getSize();
|
||||
_overrideList.add( lfo );
|
||||
}
|
||||
|
||||
/*
|
||||
* An array of LFOData that is parallel to rgLfo. The number of
|
||||
* elements that are contained in this array is specified by lfoMac.
|
||||
* -- Page 424 of 621. [MS-DOC] -- v20110315 Word (.doc) Binary File
|
||||
* Format
|
||||
*/
|
||||
for ( int x = 0; x < lfoMac; x++ )
|
||||
{
|
||||
ListFormatOverride lfo = _overrideList.get( x );
|
||||
LFOData lfoData = new LFOData( tableStream, offset,
|
||||
lfo.numOverrides() );
|
||||
lfo.setLfoData( lfoData );
|
||||
offset += lfoData.getSizeInBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int addList(ListData lst, ListFormatOverride override)
|
||||
{
|
||||
int lsid = lst.getLsid();
|
||||
while (_listMap.get(Integer.valueOf(lsid)) != null)
|
||||
{
|
||||
lsid = lst.resetListID();
|
||||
override.setLsid(lsid);
|
||||
}
|
||||
_listMap.put(Integer.valueOf(lsid), lst);
|
||||
_overrideList.add(override);
|
||||
return lsid;
|
||||
this._plfLfo = new PlfLfo( tableStream, fcPlfLfo, lcbPlfLfo );
|
||||
}
|
||||
|
||||
public void writeListDataTo( FileInformationBlock fib,
|
||||
HWPFOutputStream tableStream ) throws IOException
|
||||
{
|
||||
final int startOffset = tableStream.getOffset();
|
||||
fib.setFcPlcfLst( startOffset );
|
||||
fib.setFcPlfLst( startOffset );
|
||||
|
||||
int listSize = _listMap.size();
|
||||
|
||||
@ -156,7 +95,7 @@ public final class ListTables
|
||||
LittleEndian.putShort(shortHolder, (short)listSize);
|
||||
tableStream.write(shortHolder);
|
||||
|
||||
for(Integer x : _listMap.sortedKeys()) {
|
||||
for(Integer x : _listMap.keySet()) {
|
||||
ListData lst = _listMap.get(x);
|
||||
tableStream.write(lst.toByteArray());
|
||||
ListLevel[] lvls = lst.getLevels();
|
||||
@ -171,59 +110,35 @@ public final class ListTables
|
||||
* account for the array of LVLs. -- Page 76 of 621 -- [MS-DOC] --
|
||||
* v20110315 Word (.doc) Binary File Format
|
||||
*/
|
||||
fib.setLcbPlcfLst( tableStream.getOffset() - startOffset );
|
||||
fib.setLcbPlfLst( tableStream.getOffset() - startOffset );
|
||||
tableStream.write( levelBuf.toByteArray() );
|
||||
}
|
||||
|
||||
public void writeListOverridesTo( HWPFOutputStream tableStream )
|
||||
throws IOException
|
||||
public void writeListOverridesTo( FileInformationBlock fib,
|
||||
HWPFOutputStream tableStream ) throws IOException
|
||||
{
|
||||
LittleEndian.putUInt( _overrideList.size(), tableStream );
|
||||
|
||||
for ( ListFormatOverride lfo : _overrideList )
|
||||
{
|
||||
tableStream.write( lfo.getLfo().serialize() );
|
||||
_plfLfo.writeTo( fib, tableStream );
|
||||
}
|
||||
|
||||
for ( ListFormatOverride lfo : _overrideList )
|
||||
public LFO getLfo( int ilfo ) throws NoSuchElementException
|
||||
{
|
||||
lfo.getLfoData().writeTo( tableStream );
|
||||
}
|
||||
return _plfLfo.getLfo( ilfo );
|
||||
}
|
||||
|
||||
public ListFormatOverride getOverride(int lfoIndex)
|
||||
public LFOData getLfoData( int ilfo ) throws NoSuchElementException
|
||||
{
|
||||
return _overrideList.get(lfoIndex - 1);
|
||||
return _plfLfo.getLfoData( ilfo );
|
||||
}
|
||||
|
||||
public int getOverrideCount() {
|
||||
return _overrideList.size();
|
||||
public int getOverrideIndexFromListID( int lsid )
|
||||
throws NoSuchElementException
|
||||
{
|
||||
return _plfLfo.getIlfoByLsid( lsid );
|
||||
}
|
||||
|
||||
public int getOverrideIndexFromListID(int lstid)
|
||||
public ListLevel getLevel(int lsid, int level)
|
||||
{
|
||||
int returnVal = -1;
|
||||
int size = _overrideList.size();
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
ListFormatOverride next = _overrideList.get(x);
|
||||
if (next.getLsid() == lstid)
|
||||
{
|
||||
// 1-based index I think
|
||||
returnVal = x+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (returnVal == -1)
|
||||
{
|
||||
throw new NoSuchElementException("No list found with the specified ID");
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
public ListLevel getLevel(int listID, int level)
|
||||
{
|
||||
ListData lst = _listMap.get(Integer.valueOf(listID));
|
||||
ListData lst = _listMap.get(Integer.valueOf(lsid));
|
||||
if(level < lst.numLevels()) {
|
||||
ListLevel lvl = lst.getLevels()[level];
|
||||
return lvl;
|
||||
@ -232,112 +147,69 @@ public final class ListTables
|
||||
return null;
|
||||
}
|
||||
|
||||
public ListData getListData(int listID)
|
||||
public ListData getListData(int lsid)
|
||||
{
|
||||
return _listMap.get(Integer.valueOf(listID));
|
||||
return _listMap.get(Integer.valueOf(lsid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result
|
||||
+ ( ( _listMap == null ) ? 0 : _listMap.hashCode() );
|
||||
result = prime * result
|
||||
+ ( ( _plfLfo == null ) ? 0 : _plfLfo.hashCode() );
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals( Object obj )
|
||||
{
|
||||
if ( this == obj )
|
||||
return true;
|
||||
if ( obj == null )
|
||||
return false;
|
||||
if ( getClass() != obj.getClass() )
|
||||
return false;
|
||||
ListTables other = (ListTables) obj;
|
||||
if ( _listMap == null )
|
||||
{
|
||||
if ( other._listMap != null )
|
||||
return false;
|
||||
}
|
||||
|
||||
ListTables tables = (ListTables)obj;
|
||||
|
||||
if (_listMap.size() == tables._listMap.size())
|
||||
{
|
||||
Iterator<Integer> it = _listMap.keySet().iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
Integer key = it.next();
|
||||
ListData lst1 = _listMap.get(key);
|
||||
ListData lst2 = tables._listMap.get(key);
|
||||
if (!lst1.equals(lst2))
|
||||
else if ( !_listMap.equals( other._listMap ) )
|
||||
return false;
|
||||
if ( _plfLfo == null )
|
||||
{
|
||||
if ( other._plfLfo != null )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int size = _overrideList.size();
|
||||
if (size == tables._overrideList.size())
|
||||
{
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
if (!_overrideList.get(x).equals(tables._overrideList.get(x)))
|
||||
{
|
||||
else if ( !_plfLfo.equals( other._plfLfo ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class ListMap implements Map<Integer, ListData> {
|
||||
private ArrayList<Integer> keyList = new ArrayList<Integer>();
|
||||
private HashMap<Integer,ListData> parent = new HashMap<Integer,ListData>();
|
||||
private ListMap() {}
|
||||
public int addList( ListData lst, LFO lfo, LFOData lfoData )
|
||||
{
|
||||
int lsid = lst.getLsid();
|
||||
while ( _listMap.get( Integer.valueOf( lsid ) ) != null )
|
||||
{
|
||||
lsid = lst.resetListID();
|
||||
lfo.setLsid( lsid );
|
||||
}
|
||||
_listMap.put( Integer.valueOf( lsid ), lst );
|
||||
|
||||
public void clear() {
|
||||
keyList.clear();
|
||||
parent.clear();
|
||||
if ( lfo == null && lfoData != null )
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"LFO and LFOData should be specified both or noone" );
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return parent.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object value) {
|
||||
return parent.containsValue(value);
|
||||
}
|
||||
|
||||
public ListData get(Object key) {
|
||||
return parent.get(key);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return parent.isEmpty();
|
||||
}
|
||||
|
||||
public ListData put(Integer key, ListData value) {
|
||||
keyList.add(key);
|
||||
return parent.put(key, value);
|
||||
}
|
||||
|
||||
public void putAll(Map<? extends Integer, ? extends ListData> map) {
|
||||
for(Entry<? extends Integer, ? extends ListData> entry : map.entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public ListData remove(Object key) {
|
||||
keyList.remove(key);
|
||||
return parent.remove(key);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return parent.size();
|
||||
}
|
||||
|
||||
public Set<Entry<Integer, ListData>> entrySet() {
|
||||
throw new IllegalStateException("Use sortedKeys() + get() instead");
|
||||
}
|
||||
|
||||
public List<Integer> sortedKeys() {
|
||||
return Collections.unmodifiableList(keyList);
|
||||
}
|
||||
public Set<Integer> keySet() {
|
||||
throw new IllegalStateException("Use sortedKeys() instead");
|
||||
}
|
||||
|
||||
public Collection<ListData> values() {
|
||||
ArrayList<ListData> values = new ArrayList<ListData>();
|
||||
for(Integer key : keyList) {
|
||||
values.add(parent.get(key));
|
||||
}
|
||||
return values;
|
||||
if ( lfo != null )
|
||||
{
|
||||
_plfLfo.add( lfo, lfoData );
|
||||
}
|
||||
return lsid;
|
||||
}
|
||||
}
|
||||
|
221
src/scratchpad/src/org/apache/poi/hwpf/model/PlfLfo.java
Normal file
221
src/scratchpad/src/org/apache/poi/hwpf/model/PlfLfo.java
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* ====================================================================
|
||||
* 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.util.Arrays;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
|
||||
import org.apache.poi.util.ArrayUtil;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
/**
|
||||
* The PlfLfo structure contains the list format override data for the document.
|
||||
* <p>
|
||||
* Documentation quoted from Page 424 of 621. [MS-DOC] -- v20110315 Word (.doc)
|
||||
* Binary File Format
|
||||
*
|
||||
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
|
||||
*/
|
||||
public class PlfLfo
|
||||
{
|
||||
private static POILogger log = POILogFactory.getLogger( PlfLfo.class );
|
||||
|
||||
/**
|
||||
* An unsigned integer that specifies the count of elements in both the
|
||||
* rgLfo and rgLfoData arrays.
|
||||
*/
|
||||
private int _lfoMac;
|
||||
|
||||
private LFO[] _rgLfo;
|
||||
|
||||
private LFOData[] _rgLfoData;
|
||||
|
||||
|
||||
PlfLfo( byte[] tableStream, int fcPlfLfo, int lcbPlfLfo )
|
||||
{
|
||||
/*
|
||||
* The PlfLfo structure contains the list format override data for the
|
||||
* document. -- Page 424 of 621. [MS-DOC] -- v20110315 Word (.doc)
|
||||
* Binary File Format
|
||||
*/
|
||||
int offset = fcPlfLfo;
|
||||
|
||||
/*
|
||||
* lfoMac (4 bytes): An unsigned integer that specifies the count of
|
||||
* elements in both the rgLfo and rgLfoData arrays. -- Page 424 of 621.
|
||||
* [MS-DOC] -- v20110315 Word (.doc) Binary File Format
|
||||
*/
|
||||
long lfoMacLong = LittleEndian.getUInt( tableStream, offset );
|
||||
offset += LittleEndian.INT_SIZE;
|
||||
|
||||
if ( lfoMacLong > Integer.MAX_VALUE )
|
||||
{
|
||||
throw new UnsupportedOperationException(
|
||||
"Apache POI doesn't support rgLfo/rgLfoData size large than "
|
||||
+ Integer.MAX_VALUE + " elements" );
|
||||
}
|
||||
|
||||
this._lfoMac = (int) lfoMacLong;
|
||||
_rgLfo = new LFO[_lfoMac];
|
||||
_rgLfoData = new LFOData[_lfoMac];
|
||||
|
||||
/*
|
||||
* An array of LFO structures. The number of elements in this array is
|
||||
* specified by lfoMac. -- Page 424 of 621. [MS-DOC] -- v20110315 Word
|
||||
* (.doc) Binary File Format
|
||||
*/
|
||||
for ( int x = 0; x < _lfoMac; x++ )
|
||||
{
|
||||
LFO lfo = new LFO( tableStream, offset );
|
||||
offset += LFO.getSize();
|
||||
_rgLfo[x] = lfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* An array of LFOData that is parallel to rgLfo. The number of elements
|
||||
* that are contained in this array is specified by lfoMac. -- Page 424
|
||||
* of 621. [MS-DOC] -- v20110315 Word (.doc) Binary File Format
|
||||
*/
|
||||
for ( int x = 0; x < _lfoMac; x++ )
|
||||
{
|
||||
LFOData lfoData = new LFOData( tableStream, offset,
|
||||
_rgLfo[x].getClfolvl() );
|
||||
offset += lfoData.getSizeInBytes();
|
||||
_rgLfoData[x] = lfoData;
|
||||
}
|
||||
|
||||
if ( ( offset - fcPlfLfo ) != lcbPlfLfo )
|
||||
{
|
||||
log.log( POILogger.WARN, "Actual size of PlfLfo is "
|
||||
+ ( offset - fcPlfLfo ) + " bytes, but expected "
|
||||
+ lcbPlfLfo );
|
||||
}
|
||||
}
|
||||
|
||||
void add( LFO lfo, LFOData lfoData )
|
||||
{
|
||||
final int newLfoMac = _lfoMac + 1;
|
||||
|
||||
_rgLfo = ArrayUtil.copyOf( _rgLfo, new LFO[newLfoMac] );
|
||||
_rgLfo[_lfoMac + 1] = lfo;
|
||||
|
||||
_rgLfoData = ArrayUtil.copyOf( _rgLfoData, new LFOData[_lfoMac + 1] );
|
||||
_rgLfoData[_lfoMac + 1] = lfoData;
|
||||
|
||||
this._lfoMac = newLfoMac;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals( Object obj )
|
||||
{
|
||||
if ( this == obj )
|
||||
return true;
|
||||
if ( obj == null )
|
||||
return false;
|
||||
if ( getClass() != obj.getClass() )
|
||||
return false;
|
||||
PlfLfo other = (PlfLfo) obj;
|
||||
if ( _lfoMac != other._lfoMac )
|
||||
return false;
|
||||
if ( !Arrays.equals( _rgLfo, other._rgLfo ) )
|
||||
return false;
|
||||
if ( !Arrays.equals( _rgLfoData, other._rgLfoData ) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* An unsigned integer that specifies the count of elements in both the
|
||||
* rgLfo and rgLfoData arrays.
|
||||
*/
|
||||
public int getLfoMac()
|
||||
{
|
||||
return _lfoMac;
|
||||
}
|
||||
|
||||
public int getIlfoByLsid( int lsid )
|
||||
{
|
||||
for ( int i = 0; i < _lfoMac; i++ )
|
||||
{
|
||||
if ( _rgLfo[i].getLsid() == lsid )
|
||||
{
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
throw new NoSuchElementException( "LFO with lsid " + lsid
|
||||
+ " not found" );
|
||||
}
|
||||
|
||||
public LFO getLfo( int ilfo ) throws NoSuchElementException
|
||||
{
|
||||
if ( ilfo <= 0 || ilfo > _lfoMac )
|
||||
{
|
||||
throw new NoSuchElementException( "LFO with ilfo " + ilfo
|
||||
+ " not found. lfoMac is " + _lfoMac );
|
||||
}
|
||||
return _rgLfo[ilfo - 1];
|
||||
}
|
||||
|
||||
public LFOData getLfoData( int ilfo ) throws NoSuchElementException
|
||||
{
|
||||
if ( ilfo <= 0 || ilfo > _lfoMac )
|
||||
{
|
||||
throw new NoSuchElementException( "LFOData with ilfo " + ilfo
|
||||
+ " not found. lfoMac is " + _lfoMac );
|
||||
}
|
||||
return _rgLfoData[ilfo - 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + _lfoMac;
|
||||
result = prime * result + Arrays.hashCode( _rgLfo );
|
||||
result = prime * result + Arrays.hashCode( _rgLfoData );
|
||||
return result;
|
||||
}
|
||||
|
||||
void writeTo( FileInformationBlock fib, HWPFOutputStream outputStream )
|
||||
throws IOException
|
||||
{
|
||||
final int offset = outputStream.getOffset();
|
||||
fib.setFcPlfLfo( offset );
|
||||
|
||||
LittleEndian.putUInt( _lfoMac, outputStream );
|
||||
|
||||
byte[] bs = new byte[LFO.getSize() * _lfoMac];
|
||||
for ( int i = 0; i < _lfoMac; i++ )
|
||||
{
|
||||
_rgLfo[i].serialize( bs, i * LFO.getSize() );
|
||||
}
|
||||
outputStream.write( bs, 0, LFO.getSize() * _lfoMac );
|
||||
|
||||
for ( int i = 0; i < _lfoMac; i++ )
|
||||
{
|
||||
_rgLfoData[i].writeTo( outputStream );
|
||||
}
|
||||
fib.setLcbPlfLfo( outputStream.getOffset() - offset );
|
||||
}
|
||||
}
|
@ -508,7 +508,26 @@ public abstract class PAPAbstractType
|
||||
}
|
||||
|
||||
/**
|
||||
* 1-based index into the pllfo (lists structure), if non-zero.
|
||||
* "A 16-bit signed integer value that is used to determine which list
|
||||
* contains the paragraph. This value MUST be one of the following:
|
||||
*
|
||||
* 0x0000 -- This paragraph is not in a list, and any list formatting on the
|
||||
* paragraph is removed.
|
||||
*
|
||||
* 0x0001 - 0x07FE -- The value is a 1-based index into PlfLfo.rgLfo. The
|
||||
* LFO at this index defines the list that this paragraph is in.
|
||||
*
|
||||
* 0xF801 -- This paragraph is not in a list.
|
||||
*
|
||||
* 0xF802 - 0xFFFF -- The value is the negation of a 1-based index into
|
||||
* PlfLfo.rgLfo. The LFO at this index defines the list that this paragraph
|
||||
* is in. The logical left indentation (see sprmPDxaLeft) and the logical
|
||||
* left first line indentation (see sprmPDxaLeft1) of the paragraph MUST be
|
||||
* preserved despite any list formatting.
|
||||
*
|
||||
* By default, a paragraph is not in a list."
|
||||
*
|
||||
* Quote from [MS-DOC] -- v20110315, page 125
|
||||
*/
|
||||
@Internal
|
||||
public int getIlfo()
|
||||
@ -517,7 +536,25 @@ public abstract class PAPAbstractType
|
||||
}
|
||||
|
||||
/**
|
||||
* 1-based index into the pllfo (lists structure), if non-zero.
|
||||
* "A 16-bit signed integer value that is used to determine which list
|
||||
* contains the paragraph. This value MUST be one of the following:
|
||||
*
|
||||
* 0x0000 -- This paragraph is not in a list, and any list formatting on the
|
||||
* paragraph is removed.
|
||||
*
|
||||
* 0x0001 - 0x07FE -- The value is a 1-based index into PlfLfo.rgLfo. The
|
||||
* LFO at this index defines the list that this paragraph is in.
|
||||
*
|
||||
* 0xF801 -- This paragraph is not in a list.
|
||||
*
|
||||
* 0xF802 - 0xFFFF -- The value is the negation of a 1-based index into
|
||||
* PlfLfo.rgLfo. The LFO at this index defines the list that this paragraph
|
||||
* is in. The logical left indentation (see sprmPDxaLeft) and the logical
|
||||
* left first line indentation (see sprmPDxaLeft1) of the paragraph MUST be
|
||||
* preserved despite any list formatting. By default, a paragraph is not in
|
||||
* a list."
|
||||
*
|
||||
* Quote from [MS-DOC] -- v20110315, page 125
|
||||
*/
|
||||
@Internal
|
||||
public void setIlfo( int field_9_ilfo )
|
||||
|
@ -17,55 +17,22 @@
|
||||
|
||||
package org.apache.poi.hwpf.usermodel;
|
||||
|
||||
import org.apache.poi.hwpf.model.ListFormatOverride;
|
||||
import org.apache.poi.hwpf.model.ListFormatOverrideLevel;
|
||||
import org.apache.poi.hwpf.model.ListLevel;
|
||||
import org.apache.poi.hwpf.model.ListTables;
|
||||
import org.apache.poi.hwpf.model.PAPX;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
public final class ListEntry
|
||||
extends Paragraph
|
||||
public final class ListEntry extends Paragraph
|
||||
{
|
||||
private static POILogger log = POILogFactory.getLogger(ListEntry.class);
|
||||
|
||||
ListLevel _level;
|
||||
ListFormatOverrideLevel _overrideLevel;
|
||||
|
||||
@Internal
|
||||
ListEntry( PAPX papx, ParagraphProperties properties, Range parent )
|
||||
{
|
||||
super( papx, properties, parent );
|
||||
|
||||
final ListTables tables = parent._doc.getListTables();
|
||||
if ( tables != null && _props.getIlfo() < tables.getOverrideCount() )
|
||||
{
|
||||
ListFormatOverride override = tables.getOverride( _props.getIlfo() );
|
||||
_overrideLevel = override.getOverrideLevel( _props.getIlvl() );
|
||||
_level = tables.getLevel( override.getLsid(), _props.getIlvl() );
|
||||
}
|
||||
else
|
||||
{
|
||||
log.log( POILogger.WARN,
|
||||
"No ListTables found for ListEntry - document probably partly corrupt, "
|
||||
+ "and you may experience problems" );
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
ListEntry( PAPX papx, Range parent, ListTables tables )
|
||||
{
|
||||
super( papx, parent );
|
||||
|
||||
if(tables != null && _props.getIlfo() < tables.getOverrideCount()) {
|
||||
ListFormatOverride override = tables.getOverride(_props.getIlfo());
|
||||
_overrideLevel = override.getOverrideLevel(_props.getIlvl());
|
||||
_level = tables.getLevel(override.getLsid(), _props.getIlvl());
|
||||
} else {
|
||||
log.log(POILogger.WARN, "No ListTables found for ListEntry - document probably partly corrupt, and you may experience problems");
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
|
@ -17,9 +17,10 @@
|
||||
|
||||
package org.apache.poi.hwpf.usermodel;
|
||||
|
||||
import org.apache.poi.hwpf.HWPFDocumentCore;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.apache.poi.hwpf.model.ListFormatOverride;
|
||||
import org.apache.poi.hwpf.HWPFDocumentCore;
|
||||
import org.apache.poi.hwpf.model.LFO;
|
||||
import org.apache.poi.hwpf.model.ListLevel;
|
||||
import org.apache.poi.hwpf.model.ListTables;
|
||||
import org.apache.poi.hwpf.model.PAPX;
|
||||
@ -28,8 +29,14 @@ import org.apache.poi.hwpf.sprm.ParagraphSprmUncompressor;
|
||||
import org.apache.poi.hwpf.sprm.SprmBuffer;
|
||||
import org.apache.poi.hwpf.sprm.TableSprmCompressor;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
public class Paragraph extends Range implements Cloneable
|
||||
{
|
||||
|
||||
private static POILogger log = POILogFactory.getLogger( Paragraph.class );
|
||||
|
||||
public class Paragraph extends Range implements Cloneable {
|
||||
public final static short SPRM_JC = 0x2403;
|
||||
public final static short SPRM_FSIDEBYSIDE = 0x2404;
|
||||
public final static short SPRM_FKEEP = 0x2405;
|
||||
@ -105,10 +112,20 @@ public class Paragraph extends Range implements Cloneable {
|
||||
|
||||
if ( properties.getIlfo() != 0 && listTables != null )
|
||||
{
|
||||
final ListFormatOverride listFormatOverride = listTables
|
||||
.getOverride( properties.getIlfo() );
|
||||
final ListLevel listLevel = listTables.getLevel(
|
||||
listFormatOverride.getLsid(), properties.getIlvl() );
|
||||
LFO lfo = null;
|
||||
try
|
||||
{
|
||||
lfo = listTables.getLfo( properties.getIlfo() );
|
||||
}
|
||||
catch ( NoSuchElementException exc )
|
||||
{
|
||||
log.log( POILogger.WARN, "Paragraph refers to LFO #",
|
||||
properties.getIlfo(), " that does not exists" );
|
||||
}
|
||||
if ( lfo != null )
|
||||
{
|
||||
final ListLevel listLevel = listTables.getLevel( lfo.getLsid(),
|
||||
properties.getIlvl() );
|
||||
|
||||
if ( listLevel != null && listLevel.getGrpprlPapx() != null )
|
||||
{
|
||||
@ -121,6 +138,7 @@ public class Paragraph extends Range implements Cloneable {
|
||||
properties, papx.getGrpprl(), 2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( properties.getIlfo() > 0 )
|
||||
return new ListEntry( papx, properties, parent );
|
||||
@ -576,6 +594,22 @@ public short getStyleIndex()
|
||||
return _props.getRgdxaTab();
|
||||
}
|
||||
|
||||
public HWPFList getList()
|
||||
{
|
||||
if ( getIlfo() == 0x000 || getIlfo() == 0xF801 )
|
||||
{
|
||||
throw new IllegalStateException( "Paragraph not in list" );
|
||||
}
|
||||
HWPFList hwpfList = new HWPFList( getDocument().getStyleSheet(),
|
||||
getDocument().getListTables(), getIlfo() );
|
||||
return hwpfList;
|
||||
}
|
||||
|
||||
public boolean isInList()
|
||||
{
|
||||
return getIlfo() != 0x000 && getIlfo() != 0xF801;
|
||||
}
|
||||
|
||||
/**
|
||||
* clone the ParagraphProperties object associated with this Paragraph so
|
||||
* that you can apply the same properties to another paragraph.
|
||||
|
@ -35,21 +35,21 @@ public final class TestListTables
|
||||
FileInformationBlock fib = _hWPFDocFixture._fib;
|
||||
byte[] tableStream = _hWPFDocFixture._tableStream;
|
||||
|
||||
int listOffset = fib.getFcPlcfLst();
|
||||
int listOffset = fib.getFcPlfLst();
|
||||
int lfoOffset = fib.getFcPlfLfo();
|
||||
if (listOffset != 0 && fib.getLcbPlcfLst() != 0)
|
||||
if (listOffset != 0 && fib.getLcbPlfLst() != 0)
|
||||
{
|
||||
ListTables listTables = new ListTables (tableStream, fib.getFcPlcfLst (),
|
||||
fib.getFcPlfLfo ());
|
||||
ListTables listTables = new ListTables (tableStream, fib.getFcPlfLst(),
|
||||
fib.getFcPlfLfo (), fib.getLcbPlfLfo());
|
||||
HWPFFileSystem fileSys = new HWPFFileSystem ();
|
||||
|
||||
HWPFOutputStream tableOut = fileSys.getStream ("1Table");
|
||||
|
||||
listTables.writeListDataTo (fib, tableOut);
|
||||
int offset = tableOut.getOffset ();
|
||||
listTables.writeListOverridesTo (tableOut);
|
||||
listTables.writeListOverridesTo( fib, tableOut);
|
||||
|
||||
ListTables newTables = new ListTables (tableOut.toByteArray (), 0, offset);
|
||||
ListTables newTables = new ListTables (tableOut.toByteArray (), fib.getFcPlfLst(),
|
||||
fib.getFcPlfLfo (), fib.getLcbPlfLfo());
|
||||
|
||||
assertEquals(listTables, newTables);
|
||||
|
||||
|
@ -16,13 +16,13 @@
|
||||
==================================================================== */
|
||||
package org.apache.poi.hwpf.usermodel;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hwpf.HWPFDocument;
|
||||
import org.apache.poi.hwpf.HWPFTestDataSamples;
|
||||
import org.apache.poi.hwpf.model.ListFormatOverride;
|
||||
import org.apache.poi.hwpf.model.LFO;
|
||||
import org.apache.poi.hwpf.model.ListLevel;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class TestBug50075 extends TestCase
|
||||
{
|
||||
|
||||
@ -31,7 +31,7 @@ public class TestBug50075 extends TestCase
|
||||
Range range = doc.getRange();
|
||||
assertEquals(1, range.numParagraphs());
|
||||
ListEntry entry = (ListEntry) range.getParagraph(0);
|
||||
ListFormatOverride override = doc.getListTables().getOverride(entry.getIlfo());
|
||||
LFO override = doc.getListTables().getLfo( entry.getIlfo());
|
||||
ListLevel level = doc.getListTables().getLevel(override.getLsid(), entry.getIlvl());
|
||||
|
||||
// the bug reproduces, if this call fails with NullPointerException
|
||||
|
@ -21,19 +21,20 @@
|
||||
<suffix>AbstractType</suffix>
|
||||
<extends>HDFType</extends>
|
||||
<description>List Format Override (LFO). <p>Class and fields descriptions are quoted from
|
||||
Microsoft Office Word 97-2007 Binary File Format
|
||||
[MS-DOC] --v20110315; Word (.doc) Binary File Format
|
||||
</description>
|
||||
<author>Sergey Vladimirov; according to Microsoft Office Word 97-2007 Binary File Format
|
||||
Specification [*.doc]
|
||||
<author>Sergey Vladimirov; according to [MS-DOC] --v20110315; Word (.doc) Binary File Format;
|
||||
Copyright (c) Microsoft Corporation
|
||||
</author>
|
||||
<fields>
|
||||
<field type="int" size="4" name="lsid" description="List ID of corresponding LSTF (see LSTF)"/>
|
||||
<field type="int" size="4" name="reserved1" description="Reserved"/>
|
||||
<field type="int" size="4" name="reserved2" description="Reserved"/>
|
||||
<field type="int" size="4" name="lsid"
|
||||
description="A signed integer that specifies the list identifier of an LSTF. This LFO corresponds to the LSTF in PlfLst.rgLstf that has an lsid whose value is equal to this value."/>
|
||||
<field type="int" size="4" name="unused1" description="This field MUST be ignored"/>
|
||||
<field type="int" size="4" name="unused2" description="This field MUST be ignored"/>
|
||||
<field type="byte" size="1" name="clfolvl"
|
||||
description="Count of levels whose format is overridden (see LFOLVL)"/>
|
||||
description="An unsigned integer that specifies the field that this LFO represents."/>
|
||||
<field type="byte" size="1" name="ibstFltAutoNum" description="Used for AUTONUM field emulation"/>
|
||||
<field type="Grfhic" size="1" name="grfhic" description="HTML compatibility flags"/>
|
||||
<field type="byte" size="1" name="reserved3" description="Reserved"/>
|
||||
<field type="byte" size="1" name="unused3" description="This field MUST be ignored"/>
|
||||
</fields>
|
||||
</record>
|
||||
|
Loading…
Reference in New Issue
Block a user