==================================================================== */
package org.apache.poi.hwpf.converter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.HWPFDocumentCore;
import org.apache.poi.hwpf.converter.FontReplacer.Triplet;
-import org.apache.poi.hwpf.model.FieldsDocumentPart;
import org.apache.poi.hwpf.model.Field;
+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.Paragraph;
import org.apache.poi.hwpf.usermodel.Picture;
private static final POILogger logger = POILogFactory
.getLogger( AbstractWordConverter.class );
+ private final Set<Bookmark> bookmarkStack = new LinkedHashSet<Bookmark>();
+
private FontReplacer fontReplacer = new DefaultFontReplacer();
protected Triplet getCharacterRunTriplet( CharacterRun characterRun )
protected abstract void outputCharacters( Element block,
CharacterRun characterRun, String text );
- protected boolean processCharacters( HWPFDocumentCore hwpfDocument,
+ /**
+ * Wrap range into bookmark(s) and process it. All bookmarks have starts
+ * equal to range start and ends equal to range end. Usually it's only one
+ * bookmark.
+ */
+ protected abstract void processBookmarks( HWPFDocumentCore wordDocument,
+ Element currentBlock, Range range, int currentTableLevel,
+ List<Bookmark> rangeBookmarks );
+
+ protected boolean processCharacters( HWPFDocumentCore document,
int currentTableLevel, Range range, final Element block )
{
if ( range == null )
boolean haveAnyText = false;
+ if ( document instanceof HWPFDocument )
+ {
+ final HWPFDocument doc = (HWPFDocument) document;
+ Map<Integer, List<Bookmark>> rangeBookmarks = doc.getBookmarks()
+ .getBookmarksStartedBetween( range.getStartOffset(),
+ range.getEndOffset() );
+
+ if ( rangeBookmarks != null && !rangeBookmarks.isEmpty() )
+ {
+ boolean processedAny = processRangeBookmarks( doc,
+ currentTableLevel, range, block, rangeBookmarks );
+ if ( processedAny )
+ return true;
+ }
+ }
+
for ( int c = 0; c < range.numCharacterRuns(); c++ )
{
CharacterRun characterRun = range.getCharacterRun( c );
if ( characterRun == null )
throw new AssertionError();
- if ( hwpfDocument instanceof HWPFDocument
- && ( (HWPFDocument) hwpfDocument ).getPicturesTable()
+ if ( document instanceof HWPFDocument
+ && ( (HWPFDocument) document ).getPicturesTable()
.hasPicture( characterRun ) )
{
- HWPFDocument newFormat = (HWPFDocument) hwpfDocument;
+ HWPFDocument newFormat = (HWPFDocument) document;
Picture picture = newFormat.getPicturesTable().extractPicture(
characterRun, true );
if ( text.getBytes()[0] == FIELD_BEGIN_MARK )
{
- if ( hwpfDocument instanceof HWPFDocument )
+ if ( document instanceof HWPFDocument )
{
- Field aliveField = ( (HWPFDocument) hwpfDocument )
+ Field aliveField = ( (HWPFDocument) document )
.getFieldsTables().lookupFieldByStartOffset(
FieldsDocumentPart.MAIN,
characterRun.getStartOffset() );
if ( aliveField != null )
{
- processField( ( (HWPFDocument) hwpfDocument ), range,
+ processField( ( (HWPFDocument) document ), range,
currentTableLevel, aliveField, block );
int continueAfter = aliveField.getFieldEndOffset();
}
}
- int skipTo = tryDeadField( hwpfDocument, range,
- currentTableLevel, c, block );
+ int skipTo = tryDeadField( document, range, currentTableLevel,
+ c, block );
if ( skipTo != c )
{
Element parentFopElement, int currentTableLevel,
Paragraph paragraph, String bulletText );
+ private boolean processRangeBookmarks( HWPFDocumentCore document,
+ int currentTableLevel, Range range, final Element block,
+ Map<Integer, List<Bookmark>> rangeBookmakrs )
+ {
+ final int startOffset = range.getStartOffset();
+ final int endOffset = range.getEndOffset();
+
+ int beforeBookmarkStart = startOffset;
+ for ( Map.Entry<Integer, List<Bookmark>> entry : rangeBookmakrs
+ .entrySet() )
+ {
+ final List<Bookmark> startedAt = entry.getValue();
+
+ final List<Bookmark> bookmarks;
+ if ( entry.getKey().intValue() == startOffset
+ && !bookmarkStack.isEmpty() )
+ {
+ /*
+ * we need to filter out some bookmarks because already
+ * processing them in caller methods
+ */
+ List<Bookmark> filtered = new ArrayList<Bookmark>(
+ startedAt.size() );
+ for ( Bookmark bookmark : startedAt )
+ {
+ if ( this.bookmarkStack.contains( bookmark ) )
+ continue;
+
+ filtered.add( bookmark );
+ }
+
+ if ( filtered.isEmpty() )
+ // no bookmarks - skip to next start point
+ continue;
+
+ bookmarks = filtered;
+ }
+ else
+ {
+ bookmarks = startedAt;
+ }
+
+ // TODO: test me
+ /*
+ * we processing only bookmarks with max size, they shall be first
+ * in sorted list. Other bookmarks will be processed by called
+ * method
+ */
+ final Bookmark firstBookmark = bookmarks.iterator().next();
+ final int startBookmarkOffset = firstBookmark.getStart();
+ final int endBookmarkOffset = Math.min( firstBookmark.getEnd(),
+ range.getEndOffset() );
+ List<Bookmark> toProcess = new ArrayList<Bookmark>(
+ bookmarks.size() );
+ for ( Bookmark bookmark : bookmarks )
+ {
+ if ( Math.min( bookmark.getEnd(), range.getEndOffset() ) != endBookmarkOffset )
+ break;
+ toProcess.add( bookmark );
+ }
+
+ if ( beforeBookmarkStart != startBookmarkOffset )
+ {
+ // we have range before bookmark
+ Range beforeBookmarkRange = new Range( beforeBookmarkStart,
+ startBookmarkOffset, range )
+ {
+ @Override
+ public String toString()
+ {
+ return "BeforeBookmarkRange (" + super.toString() + ")";
+ }
+ };
+ processCharacters( document, currentTableLevel,
+ beforeBookmarkRange, block );
+ }
+ Range bookmarkRange = new Range( startBookmarkOffset,
+ endBookmarkOffset, range )
+ {
+ @Override
+ public String toString()
+ {
+ return "BookmarkRange (" + super.toString() + ")";
+ }
+ };
+
+ bookmarkStack.addAll( toProcess );
+ try
+ {
+ processBookmarks( document, block, bookmarkRange,
+ currentTableLevel,
+ Collections.unmodifiableList( toProcess ) );
+ }
+ finally
+ {
+ bookmarkStack.removeAll( toProcess );
+ }
+ beforeBookmarkStart = endBookmarkOffset;
+ }
+
+ if ( beforeBookmarkStart == startOffset )
+ {
+ return false;
+ }
+
+ if ( beforeBookmarkStart != endOffset )
+ {
+ // we have range after last bookmark
+ Range afterLastBookmarkRange = new Range( beforeBookmarkStart,
+ endOffset, range )
+ {
+ @Override
+ public String toString()
+ {
+ return "AfterBookmarkRange (" + super.toString() + ")";
+ }
+ };
+ processCharacters( document, currentTableLevel,
+ afterLastBookmarkRange, block );
+ }
+ return true;
+ }
+
protected abstract void processSection( HWPFDocumentCore wordDocument,
Section section, int s );
import java.io.File;
import java.io.FileWriter;
+import java.util.List;
import java.util.Stack;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.HWPFDocumentCore;
import org.apache.poi.hwpf.converter.FontReplacer.Triplet;
+import org.apache.poi.hwpf.usermodel.Bookmark;
import org.apache.poi.hwpf.usermodel.CharacterRun;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Picture;
public class WordToFoConverter extends AbstractWordConverter
{
- /**
- * Holds properties values, applied to current <tt>fo:block</tt> element.
- * Those properties shall not be doubled in children <tt>fo:inline</tt>
- * elements.
- */
- private static class BlockProperies
- {
- final boolean pBold;
- final String pFontName;
- final int pFontSize;
- final boolean pItalic;
-
- public BlockProperies( String pFontName, int pFontSize, boolean pBold,
- boolean pItalic )
- {
- this.pFontName = pFontName;
- this.pFontSize = pFontSize;
- this.pBold = pBold;
- this.pItalic = pItalic;
- }
- }
-
private static final POILogger logger = POILogFactory
.getLogger( WordToFoConverter.class );
inline.appendChild( textNode );
}
+ @Override
+ protected void processBookmarks( HWPFDocumentCore wordDocument,
+ Element currentBlock, Range range, int currentTableLevel,
+ List<Bookmark> rangeBookmarks )
+ {
+ Element parent = currentBlock;
+ for ( Bookmark bookmark : rangeBookmarks )
+ {
+ Element bookmarkElement = foDocumentFacade.createInline();
+ bookmarkElement.setAttribute( "id", bookmark.getName() );
+ parent.appendChild( bookmarkElement );
+ parent = bookmarkElement;
+ }
+
+ if ( range != null )
+ processCharacters( wordDocument, currentTableLevel, range, parent );
+ }
+
@Override
protected void processDocumentInformation(
SummaryInformation summaryInformation )
}
}
+ /**
+ * Holds properties values, applied to current <tt>fo:block</tt> element.
+ * Those properties shall not be doubled in children <tt>fo:inline</tt>
+ * elements.
+ */
+ private static class BlockProperies
+ {
+ final boolean pBold;
+ final String pFontName;
+ final int pFontSize;
+ final boolean pItalic;
+
+ public BlockProperies( String pFontName, int pFontSize, boolean pBold,
+ boolean pItalic )
+ {
+ this.pFontName = pFontName;
+ this.pFontSize = pFontSize;
+ this.pBold = pBold;
+ this.pItalic = pItalic;
+ }
+ }
+
}
private Bookmark getBookmark( final GenericPropertyNode first )
{
- return new Bookmark()
- {
- public int getEnd()
- {
- int currentIndex = bookmarksTables
- .getDescriptorFirstIndex( first );
- try
- {
- GenericPropertyNode descriptorLim = bookmarksTables
- .getDescriptorLim( currentIndex );
- return descriptorLim.getStart();
- }
- catch ( IndexOutOfBoundsException exc )
- {
- return first.getEnd();
- }
- }
-
- public String getName()
- {
- int currentIndex = bookmarksTables
- .getDescriptorFirstIndex( first );
- try
- {
- return bookmarksTables.getName( currentIndex );
- }
- catch ( ArrayIndexOutOfBoundsException exc )
- {
- return "";
- }
- }
-
- public int getStart()
- {
- return first.getStart();
- }
-
- public void setName( String name )
- {
- int currentIndex = bookmarksTables
- .getDescriptorFirstIndex( first );
- bookmarksTables.setName( currentIndex, name );
- }
- };
+ return new BookmarkImpl( first );
}
public Bookmark getBookmark( int index )
for ( int lookupIndex = startLookupIndex; lookupIndex < endLookupIndex; lookupIndex++ )
{
int s = sortedStartPositions[lookupIndex];
+ if ( s < startInclusive )
+ continue;
+ if ( s >= endExclusive )
+ break;
+
List<Bookmark> startedAt = getBookmarksAt( s );
if ( startedAt != null )
result.put( Integer.valueOf( s ), startedAt );
this.sortedDescriptors = result;
this.sortedStartPositions = indices;
}
+
+ private final class BookmarkImpl implements Bookmark
+ {
+ private final GenericPropertyNode first;
+
+ private BookmarkImpl( GenericPropertyNode first )
+ {
+ this.first = first;
+ }
+
+ @Override
+ public boolean equals( Object obj )
+ {
+ if ( this == obj )
+ return true;
+ if ( obj == null )
+ return false;
+ if ( getClass() != obj.getClass() )
+ return false;
+ BookmarkImpl other = (BookmarkImpl) obj;
+ if ( first == null )
+ {
+ if ( other.first != null )
+ return false;
+ }
+ else if ( !first.equals( other.first ) )
+ return false;
+ return true;
+ }
+
+ public int getEnd()
+ {
+ int currentIndex = bookmarksTables.getDescriptorFirstIndex( first );
+ try
+ {
+ GenericPropertyNode descriptorLim = bookmarksTables
+ .getDescriptorLim( currentIndex );
+ return descriptorLim.getStart();
+ }
+ catch ( IndexOutOfBoundsException exc )
+ {
+ return first.getEnd();
+ }
+ }
+
+ public String getName()
+ {
+ int currentIndex = bookmarksTables.getDescriptorFirstIndex( first );
+ try
+ {
+ return bookmarksTables.getName( currentIndex );
+ }
+ catch ( ArrayIndexOutOfBoundsException exc )
+ {
+ return "";
+ }
+ }
+
+ public int getStart()
+ {
+ return first.getStart();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return 31 + ( first == null ? 0 : first.hashCode() );
+ }
+
+ public void setName( String name )
+ {
+ int currentIndex = bookmarksTables.getDescriptorFirstIndex( first );
+ bookmarksTables.setName( currentIndex, name );
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Bookmark [" + getStart() + "; " + getEnd() + "): name: "
+ + getName();
+ }
+
+ }
}