1821 lines
54 KiB
Java
1821 lines
54 KiB
Java
|
|
/* ====================================================================
|
|
Copyright 2002-2004 Apache Software Foundation
|
|
|
|
Licensed 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.hdf.extractor;
|
|
|
|
|
|
import org.apache.poi.hdf.extractor.util.*;
|
|
import org.apache.poi.hdf.extractor.data.*;
|
|
import java.util.*;
|
|
import java.io.*;
|
|
|
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
|
import org.apache.poi.poifs.filesystem.DocumentEntry;
|
|
|
|
import org.apache.poi.util.LittleEndian;
|
|
|
|
/**
|
|
* This class contains the main functionality for the Word file "reader". Much
|
|
* of the code in this class is based on the Word 97 document file format. Only
|
|
* works for non-complex files
|
|
*
|
|
* @author Ryan Ackley
|
|
*/
|
|
|
|
public class WordDocument
|
|
{
|
|
/** byte buffer containing the main Document stream*/
|
|
byte[] _header;
|
|
/** contains all style information for this document see Word 97 Doc spec*/
|
|
StyleSheet _styleSheet;
|
|
/** contains All list information for this document*/
|
|
ListTables _listTables;
|
|
/** contains global Document properties for this document*/
|
|
DOP _docProps = new DOP();
|
|
|
|
int _currentList = -1;
|
|
int _tableSize;
|
|
int _sectionCounter = 1;
|
|
/** fonts available for this document*/
|
|
FontTable _fonts;
|
|
|
|
/** document's text blocks*/
|
|
BTreeSet _text = new BTreeSet();
|
|
/** document's character runs */
|
|
BTreeSet _characterTable = new BTreeSet();
|
|
/** document's paragraphs*/
|
|
BTreeSet _paragraphTable = new BTreeSet();
|
|
/** doucment's sections*/
|
|
BTreeSet _sectionTable = new BTreeSet();
|
|
|
|
/** used for XSL-FO conversion*/
|
|
StringBuffer _headerBuffer = new StringBuffer();
|
|
/** used for XSL-FO conversion*/
|
|
StringBuffer _bodyBuffer = new StringBuffer();
|
|
/** used for XSL-FO table conversion*/
|
|
StringBuffer _cellBuffer;
|
|
/** used for XSL-FO table conversion*/
|
|
ArrayList _cells;
|
|
/** used for XSL-FO table conversion*/
|
|
ArrayList _table;
|
|
|
|
/** document's header and footer information*/
|
|
byte[] _plcfHdd;
|
|
|
|
/** starting position of text in main document stream*/
|
|
int _fcMin;
|
|
/** length of main document text stream*/
|
|
int _ccpText;
|
|
/** length of footnotes text*/
|
|
int _ccpFtn;
|
|
|
|
/** The name of the file to write to */
|
|
private static String _outName;
|
|
|
|
/** OLE stuff*/
|
|
private InputStream istream;
|
|
/** OLE stuff*/
|
|
private POIFSFileSystem filesystem;
|
|
|
|
//used internally
|
|
private static int HEADER_EVEN_INDEX = 0;
|
|
private static int HEADER_ODD_INDEX = 1;
|
|
private static int FOOTER_EVEN_INDEX = 2;
|
|
private static int FOOTER_ODD_INDEX = 3;
|
|
private static int HEADER_FIRST_INDEX = 4;
|
|
private static int FOOTER_FIRST_INDEX = 5;
|
|
|
|
/**
|
|
* right now this function takes one parameter: a Word file, and outputs an
|
|
* XSL-FO document at c:\test.xml (this is hardcoded)
|
|
*/
|
|
public static void main(String args[])
|
|
{
|
|
/*try
|
|
{
|
|
WordDocument file = new WordDocument(args[0], "r");
|
|
Writer out = new BufferedWriter(new FileWriter(args[1]));
|
|
file.writeAllText(out);
|
|
out.flush();
|
|
out.close();
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
t.printStackTrace();
|
|
}*/
|
|
try
|
|
{
|
|
_outName = args[1];
|
|
WordDocument file = new WordDocument(args[0]);
|
|
file.closeDoc();
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
e.printStackTrace();
|
|
}
|
|
System.exit(0);
|
|
}
|
|
/**
|
|
* Spits out the document text
|
|
*
|
|
* @param out The Writer to write the text to.
|
|
* @throws IOException if there is a problem while reading from the file or
|
|
* writing out the text.
|
|
*/
|
|
public void writeAllText(Writer out) throws IOException
|
|
{
|
|
int textStart = Utils.convertBytesToInt(_header, 0x18);
|
|
int textEnd = Utils.convertBytesToInt(_header, 0x1c);
|
|
ArrayList textPieces = findProperties(textStart, textEnd, _text.root);
|
|
int size = textPieces.size();
|
|
|
|
for(int x = 0; x < size; x++)
|
|
{
|
|
TextPiece nextPiece = (TextPiece)textPieces.get(x);
|
|
int start = nextPiece.getStart();
|
|
int end = nextPiece.getEnd();
|
|
boolean unicode = nextPiece.usesUnicode();
|
|
int add = 1;
|
|
|
|
if(unicode)
|
|
{
|
|
add = 2;
|
|
char ch;
|
|
for(int y = start; y < end; y += add)
|
|
{
|
|
ch = (char)Utils.convertBytesToShort(_header, y);
|
|
out.write(ch);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
String sText = new String(_header, start, end-start);
|
|
out.write(sText);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Constructs a Word document from fileName. Parses the document and places
|
|
* all the important stuff into data structures.
|
|
*
|
|
* @param fileName The name of the file to read.
|
|
* @throws IOException if there is a problem while parsing the document.
|
|
*/
|
|
public WordDocument(String fileName) throws IOException
|
|
{
|
|
this(new FileInputStream(fileName));
|
|
}
|
|
|
|
public WordDocument(InputStream inputStream) throws IOException
|
|
{
|
|
//do Ole stuff
|
|
istream = inputStream;
|
|
filesystem = new POIFSFileSystem(istream);
|
|
|
|
//get important stuff from the Header block and parse all the
|
|
//data structures
|
|
readFIB();
|
|
|
|
//get the SEPS for the main document text
|
|
ArrayList sections = findProperties(_fcMin, _fcMin + _ccpText, _sectionTable.root);
|
|
|
|
//iterate through sections, paragraphs, and character runs doing what
|
|
//you will with the data.
|
|
int size = sections.size();
|
|
for(int x = 0; x < size; x++)
|
|
{
|
|
SepxNode node = (SepxNode)sections.get(x);
|
|
int start = node.getStart();
|
|
int end = node.getEnd();
|
|
SEP sep = (SEP)StyleSheet.uncompressProperty(node.getSepx(), new SEP(), _styleSheet);
|
|
writeSection(Math.max(_fcMin, start), Math.min(_fcMin + _ccpText, end), sep, _text, _paragraphTable, _characterTable, _styleSheet);
|
|
}
|
|
//finish
|
|
istream.close();
|
|
|
|
}
|
|
/**
|
|
* Extracts the main document stream from the POI file then hands off to other
|
|
* functions that parse other areas.
|
|
*
|
|
* @throws IOException
|
|
*/
|
|
private void readFIB() throws IOException
|
|
{
|
|
//get the main document stream
|
|
DocumentEntry headerProps =
|
|
(DocumentEntry)filesystem.getRoot().getEntry("WordDocument");
|
|
|
|
//I call it the header but its also the main document stream
|
|
_header = new byte[headerProps.getSize()];
|
|
filesystem.createDocumentInputStream("WordDocument").read(_header);
|
|
|
|
//Get the information we need from the header
|
|
int info = LittleEndian.getShort(_header, 0xa);
|
|
|
|
_fcMin = LittleEndian.getInt(_header, 0x18);
|
|
_ccpText = LittleEndian.getInt(_header, 0x4c);
|
|
_ccpFtn = LittleEndian.getInt(_header, 0x50);
|
|
|
|
int charPLC = LittleEndian.getInt(_header, 0xfa);
|
|
int charPlcSize = LittleEndian.getInt(_header, 0xfe);
|
|
int parPLC = LittleEndian.getInt(_header, 0x102);
|
|
int parPlcSize = LittleEndian.getInt(_header, 0x106);
|
|
boolean useTable1 = (info & 0x200) != 0;
|
|
|
|
//process the text and formatting properties
|
|
processComplexFile(useTable1, charPLC, charPlcSize, parPLC, parPlcSize);
|
|
}
|
|
|
|
/**
|
|
* Extracts the correct Table stream from the POI filesystem then hands off to
|
|
* other functions to process text and formatting info. the name is based on
|
|
* the fact that in Word 8(97) all text (not character or paragraph formatting)
|
|
* is stored in complex format.
|
|
*
|
|
* @param useTable1 boolean that specifies if we should use table1 or table0
|
|
* @param charTable offset in table stream of character property bin table
|
|
* @param charPlcSize size of character property bin table
|
|
* @param parTable offset in table stream of paragraph property bin table.
|
|
* @param parPlcSize size of paragraph property bin table.
|
|
* @return boolean indocating success of
|
|
* @throws IOException
|
|
*/
|
|
private void processComplexFile(boolean useTable1, int charTable,
|
|
int charPlcSize, int parTable, int parPlcSize) throws IOException
|
|
{
|
|
|
|
//get the location of the piece table
|
|
int complexOffset = LittleEndian.getInt(_header, 0x1a2);
|
|
|
|
String tablename=null;
|
|
DocumentEntry tableEntry = null;
|
|
if(useTable1)
|
|
{
|
|
tablename="1Table";
|
|
}
|
|
else
|
|
{
|
|
tablename="0Table";
|
|
}
|
|
tableEntry = (DocumentEntry)filesystem.getRoot().getEntry(tablename);
|
|
|
|
//load the table stream into a buffer
|
|
int size = tableEntry.getSize();
|
|
byte[] tableStream = new byte[size];
|
|
filesystem.createDocumentInputStream(tablename).read(tableStream);
|
|
|
|
//init the DOP for this document
|
|
initDocProperties(tableStream);
|
|
//load the header/footer raw data for this document
|
|
initPclfHdd(tableStream);
|
|
//parse out the text locations
|
|
findText(tableStream, complexOffset);
|
|
//parse out text formatting
|
|
findFormatting(tableStream, charTable, charPlcSize, parTable, parPlcSize);
|
|
|
|
}
|
|
/**
|
|
* Goes through the piece table and parses out the info regarding the text
|
|
* blocks. For Word 97 and greater all text is stored in the "complex" way
|
|
* because of unicode.
|
|
*
|
|
* @param tableStream buffer containing the main table stream.
|
|
* @param beginning of the complex data.
|
|
* @throws IOException
|
|
*/
|
|
private void findText(byte[] tableStream, int complexOffset) throws IOException
|
|
{
|
|
//actual text
|
|
int pos = complexOffset;
|
|
//skips through the prms before we reach the piece table. These contain data
|
|
//for actual fast saved files
|
|
while(tableStream[pos] == 1)
|
|
{
|
|
pos++;
|
|
int skip = LittleEndian.getShort(tableStream, pos);
|
|
pos += 2 + skip;
|
|
}
|
|
if(tableStream[pos] != 2)
|
|
{
|
|
throw new IOException("corrupted Word file");
|
|
}
|
|
else
|
|
{
|
|
//parse out the text pieces
|
|
int pieceTableSize = LittleEndian.getInt(tableStream, ++pos);
|
|
pos += 4;
|
|
int pieces = (pieceTableSize - 4) / 12;
|
|
for (int x = 0; x < pieces; x++)
|
|
{
|
|
int filePos = LittleEndian.getInt(tableStream, pos + ((pieces + 1) * 4) + (x * 8) + 2);
|
|
boolean unicode = false;
|
|
if ((filePos & 0x40000000) == 0)
|
|
{
|
|
unicode = true;
|
|
}
|
|
else
|
|
{
|
|
unicode = false;
|
|
filePos &= ~(0x40000000);//gives me FC in doc stream
|
|
filePos /= 2;
|
|
}
|
|
int totLength = LittleEndian.getInt(tableStream, pos + (x + 1) * 4) -
|
|
LittleEndian.getInt(tableStream, pos + (x * 4));
|
|
|
|
TextPiece piece = new TextPiece(filePos, totLength, unicode);
|
|
_text.add(piece);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Does all of the formatting parsing
|
|
*
|
|
* @param tableStream Main table stream buffer.
|
|
* @param charOffset beginning of the character bin table.
|
|
* @param chrPlcSize size of the char bin table.
|
|
* @param parOffset offset of the paragraph bin table.
|
|
* @param size of the paragraph bin table.
|
|
*/
|
|
private void findFormatting(byte[] tableStream, int charOffset,
|
|
int charPlcSize, int parOffset, int parPlcSize) throws IOException
|
|
{
|
|
openDoc();
|
|
createStyleSheet(tableStream);
|
|
createListTables(tableStream);
|
|
createFontTable(tableStream);
|
|
|
|
//find character runs
|
|
//Get all the chpx info and store it
|
|
|
|
int arraySize = (charPlcSize - 4)/8;
|
|
|
|
//first we must go through the bin table and find the fkps
|
|
for(int x = 0; x < arraySize; x++)
|
|
{
|
|
|
|
|
|
//get page number(has nothing to do with document page)
|
|
//containing the chpx for the paragraph
|
|
int PN = LittleEndian.getInt(tableStream, charOffset + (4 * (arraySize + 1) + (4 * x)));
|
|
|
|
byte[] fkp = new byte[512];
|
|
System.arraycopy(_header, (PN * 512), fkp, 0, 512);
|
|
//take each fkp and get the chpxs
|
|
int crun = Utils.convertUnsignedByteToInt(fkp[511]);
|
|
for(int y = 0; y < crun; y++)
|
|
{
|
|
//get the beginning fc of each paragraph text run
|
|
int fcStart = LittleEndian.getInt(fkp, y * 4);
|
|
int fcEnd = LittleEndian.getInt(fkp, (y+1) * 4);
|
|
//get the offset in fkp of the papx for this paragraph
|
|
int chpxOffset = 2 * Utils.convertUnsignedByteToInt(fkp[((crun + 1) * 4) + y]);
|
|
|
|
//optimization if offset == 0 use "Normal" style
|
|
if(chpxOffset == 0)
|
|
|
|
{
|
|
_characterTable.add(new ChpxNode(fcStart, fcEnd, new byte[0]));
|
|
continue;
|
|
}
|
|
|
|
int size = Utils.convertUnsignedByteToInt(fkp[chpxOffset]);
|
|
|
|
byte[] chpx = new byte[size];
|
|
System.arraycopy(fkp, ++chpxOffset, chpx, 0, size);
|
|
//_papTable.put(new Integer(fcStart), papx);
|
|
_characterTable.add(new ChpxNode(fcStart, fcEnd, chpx));
|
|
}
|
|
|
|
}
|
|
|
|
//find paragraphs
|
|
arraySize = (parPlcSize - 4)/8;
|
|
//first we must go through the bin table and find the fkps
|
|
for(int x = 0; x < arraySize; x++)
|
|
{
|
|
int PN = LittleEndian.getInt(tableStream, parOffset + (4 * (arraySize + 1) + (4 * x)));
|
|
|
|
byte[] fkp = new byte[512];
|
|
System.arraycopy(_header, (PN * 512), fkp, 0, 512);
|
|
//take each fkp and get the paps
|
|
int crun = Utils.convertUnsignedByteToInt(fkp[511]);
|
|
for(int y = 0; y < crun; y++)
|
|
{
|
|
//get the beginning fc of each paragraph text run
|
|
int fcStart = LittleEndian.getInt(fkp, y * 4);
|
|
int fcEnd = LittleEndian.getInt(fkp, (y+1) * 4);
|
|
//get the offset in fkp of the papx for this paragraph
|
|
int papxOffset = 2 * Utils.convertUnsignedByteToInt(fkp[((crun + 1) * 4) + (y * 13)]);
|
|
int size = 2 * Utils.convertUnsignedByteToInt(fkp[papxOffset]);
|
|
if(size == 0)
|
|
{
|
|
size = 2 * Utils.convertUnsignedByteToInt(fkp[++papxOffset]);
|
|
}
|
|
else
|
|
{
|
|
size--;
|
|
}
|
|
|
|
byte[] papx = new byte[size];
|
|
System.arraycopy(fkp, ++papxOffset, papx, 0, size);
|
|
_paragraphTable.add(new PapxNode(fcStart, fcEnd, papx));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//find sections
|
|
int fcMin = Utils.convertBytesToInt(_header, 0x18);
|
|
int plcfsedFC = Utils.convertBytesToInt(_header, 0xca);
|
|
int plcfsedSize = Utils.convertBytesToInt(_header, 0xce);
|
|
byte[] plcfsed = new byte[plcfsedSize];
|
|
System.arraycopy(tableStream, plcfsedFC, plcfsed, 0, plcfsedSize);
|
|
|
|
arraySize = (plcfsedSize - 4)/16;
|
|
|
|
//openDoc();
|
|
|
|
for(int x = 0; x < arraySize; x++)
|
|
{
|
|
int sectionStart = Utils.convertBytesToInt(plcfsed, x * 4) + fcMin;
|
|
int sectionEnd = Utils.convertBytesToInt(plcfsed, (x+1) * 4) + fcMin;
|
|
int sepxStart = Utils.convertBytesToInt(plcfsed, 4 * (arraySize + 1) + (x * 12) + 2);
|
|
int sepxSize = Utils.convertBytesToShort(_header, sepxStart);
|
|
byte[] sepx = new byte[sepxSize];
|
|
System.arraycopy(_header, sepxStart + 2, sepx, 0, sepxSize);
|
|
SepxNode node = new SepxNode(x + 1, sectionStart, sectionEnd, sepx);
|
|
_sectionTable.add(node);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public void openDoc()
|
|
{
|
|
_headerBuffer.append("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\r\n");
|
|
_headerBuffer.append("<fo:root xmlns:fo=\"http://www.w3.org/1999/XSL/Format\">\r\n");
|
|
_headerBuffer.append("<fo:layout-master-set>\r\n");
|
|
|
|
}
|
|
private HeaderFooter findSectionHdrFtr(int type, int index)
|
|
{
|
|
if(_plcfHdd.length < 50)
|
|
{
|
|
return new HeaderFooter(0,0,0);
|
|
}
|
|
int start = _fcMin + _ccpText + _ccpFtn;
|
|
int end = start;
|
|
int arrayIndex = 0;
|
|
|
|
switch(type)
|
|
{
|
|
case HeaderFooter.HEADER_EVEN:
|
|
arrayIndex = (HEADER_EVEN_INDEX + (index * 6));
|
|
break;
|
|
case HeaderFooter.FOOTER_EVEN:
|
|
arrayIndex = (FOOTER_EVEN_INDEX + (index * 6));
|
|
break;
|
|
case HeaderFooter.HEADER_ODD:
|
|
arrayIndex = (HEADER_ODD_INDEX + (index * 6));
|
|
break;
|
|
case HeaderFooter.FOOTER_ODD:
|
|
arrayIndex = (FOOTER_ODD_INDEX + (index * 6));
|
|
break;
|
|
case HeaderFooter.HEADER_FIRST:
|
|
arrayIndex = (HEADER_FIRST_INDEX + (index * 6));
|
|
break;
|
|
case HeaderFooter.FOOTER_FIRST:
|
|
arrayIndex = (FOOTER_FIRST_INDEX + (index * 6));
|
|
break;
|
|
}
|
|
start += Utils.convertBytesToInt(_plcfHdd, (arrayIndex * 4));
|
|
end += Utils.convertBytesToInt(_plcfHdd, (arrayIndex + 1) * 4);
|
|
|
|
HeaderFooter retValue = new HeaderFooter(type, start, end);
|
|
|
|
if((end - start) == 0 && index > 1)
|
|
{
|
|
retValue = findSectionHdrFtr(type, index - 1);
|
|
}
|
|
return retValue;
|
|
}
|
|
/**
|
|
* inits this document DOP structure.
|
|
*
|
|
* @param tableStream The documents table stream.
|
|
*/
|
|
private void initDocProperties(byte[] tableStream)
|
|
{
|
|
int pos = LittleEndian.getInt(_header, 0x192);
|
|
int size = LittleEndian.getInt(_header, 0x196);
|
|
byte[] dop = new byte[size];
|
|
|
|
System.arraycopy(tableStream, pos, dop, 0, size);
|
|
|
|
_docProps._fFacingPages = (dop[0] & 0x1) > 0;
|
|
_docProps._fpc = (dop[0] & 0x60) >> 5;
|
|
|
|
short num = LittleEndian.getShort(dop, 2);
|
|
_docProps._rncFtn = (num & 0x3);
|
|
_docProps._nFtn = (short)(num & 0xfffc) >> 2;
|
|
num = LittleEndian.getShort(dop, 52);
|
|
_docProps._rncEdn = num & 0x3;
|
|
_docProps._nEdn = (short)(num & 0xfffc) >> 2;
|
|
num = LittleEndian.getShort(dop, 54);
|
|
_docProps._epc = num & 0x3;
|
|
}
|
|
|
|
public void writeSection(int start, int end, SEP sep, BTreeSet text,
|
|
BTreeSet paragraphTable, BTreeSet characterTable,
|
|
StyleSheet stylesheet)
|
|
{
|
|
|
|
HeaderFooter titleHeader = findSectionHdrFtr(HeaderFooter.HEADER_FIRST, _sectionCounter);
|
|
HeaderFooter titleFooter = findSectionHdrFtr(HeaderFooter.FOOTER_FIRST, _sectionCounter);
|
|
HeaderFooter oddHeader = findSectionHdrFtr(HeaderFooter.HEADER_ODD, _sectionCounter);
|
|
HeaderFooter evenHeader = findSectionHdrFtr(HeaderFooter.HEADER_EVEN, _sectionCounter);
|
|
HeaderFooter oddFooter = findSectionHdrFtr(HeaderFooter.FOOTER_ODD, _sectionCounter);
|
|
HeaderFooter evenFooter = findSectionHdrFtr(HeaderFooter.FOOTER_EVEN, _sectionCounter);
|
|
|
|
String titlePage = null;
|
|
String evenPage = null;
|
|
String oddPage = null;
|
|
String regPage = null;
|
|
|
|
String sequenceName = null;
|
|
|
|
/*if(sep._fTitlePage)
|
|
{
|
|
titlePage = createPageMaster(sep, "first", _sectionCounter, createRegion("before", "title-header"), createRegion("after", "title-footer"));
|
|
|
|
if(!titleHeader.isEmpty())
|
|
{
|
|
addStaticContent("title-header" + _sectionCounter, titleHeader);
|
|
}
|
|
if(!titleFooter.isEmpty())
|
|
{
|
|
addStaticContent("title-footer" + _sectionCounter, titleFooter);
|
|
}
|
|
}*/
|
|
|
|
if(_docProps._fFacingPages)
|
|
{
|
|
if(sep._fTitlePage)
|
|
{
|
|
String before = createRegion(true, titleHeader, sep, "title-header" + _sectionCounter);
|
|
String after = createRegion(false, titleFooter, sep, "title-footer" + _sectionCounter);
|
|
titlePage = createPageMaster(sep, "first", _sectionCounter, before, after);
|
|
}
|
|
String before = createRegion(true, evenHeader, sep, "even-header" + _sectionCounter);
|
|
String after = createRegion(false, evenFooter, sep, "even-footer" + _sectionCounter);
|
|
evenPage = createPageMaster(sep, "even", _sectionCounter, before, after);
|
|
before = createRegion(true, oddHeader, sep, "odd-header" + _sectionCounter);
|
|
after = createRegion(false, oddFooter, sep, "odd-footer" + _sectionCounter);
|
|
oddPage = createPageMaster(sep, "odd", _sectionCounter, before, after);
|
|
sequenceName = createEvenOddPageSequence(titlePage, evenPage, oddPage, _sectionCounter);
|
|
|
|
openPage(sequenceName, "reference");
|
|
|
|
if(sep._fTitlePage)
|
|
{
|
|
|
|
|
|
if(!titleHeader.isEmpty())
|
|
{
|
|
addStaticContent("title-header" + _sectionCounter, titleHeader);
|
|
}
|
|
if(!titleFooter.isEmpty())
|
|
{
|
|
addStaticContent("title-footer" + _sectionCounter, titleFooter);
|
|
}
|
|
}
|
|
|
|
//handle the headers and footers for odd and even pages
|
|
if(!oddHeader.isEmpty())
|
|
{
|
|
addStaticContent("odd-header" + _sectionCounter, oddHeader);
|
|
}
|
|
if(!oddFooter.isEmpty())
|
|
{
|
|
addStaticContent("odd-footer" + _sectionCounter, oddFooter);
|
|
}
|
|
if(!evenHeader.isEmpty())
|
|
{
|
|
addStaticContent("even-header" + _sectionCounter, evenHeader);
|
|
}
|
|
if(!evenFooter.isEmpty())
|
|
{
|
|
addStaticContent("even-footer" + _sectionCounter, evenFooter);
|
|
}
|
|
openFlow();
|
|
addBlockContent(start, end, text, paragraphTable, characterTable);
|
|
closeFlow();
|
|
closePage();
|
|
}
|
|
else
|
|
{
|
|
/*if(sep._fTitlePage)
|
|
{
|
|
String before = createRegion(true, titleHeader, sep);
|
|
String after = createRegion(false, titleFooter, sep);
|
|
titlePage = createPageMaster(sep, "first", _sectionCounter, before, after);
|
|
}*/
|
|
String before = createRegion(true, oddHeader, sep, null);
|
|
String after = createRegion(false, oddFooter, sep, null);
|
|
regPage = createPageMaster(sep, "page", _sectionCounter, before, after);
|
|
|
|
if(sep._fTitlePage)
|
|
{
|
|
before = createRegion(true, titleHeader, sep, "title-header" + _sectionCounter);
|
|
after = createRegion(false, titleFooter, sep, "title-footer" + _sectionCounter);
|
|
titlePage = createPageMaster(sep, "first", _sectionCounter, before, after);
|
|
sequenceName = createPageSequence(titlePage, regPage, _sectionCounter);
|
|
openPage(sequenceName, "reference");
|
|
|
|
if(!titleHeader.isEmpty())
|
|
{
|
|
addStaticContent("title-header" + _sectionCounter, titleHeader);
|
|
}
|
|
if(!titleFooter.isEmpty())
|
|
{
|
|
addStaticContent("title-footer" + _sectionCounter, titleFooter);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
openPage(regPage, "name");
|
|
}
|
|
if(!oddHeader.isEmpty())
|
|
{
|
|
addStaticContent("xsl-region-before", oddHeader);
|
|
}
|
|
if(!oddFooter.isEmpty())
|
|
{
|
|
addStaticContent("xsl-region-after", oddFooter);
|
|
}
|
|
openFlow();
|
|
addBlockContent(start, end, text, paragraphTable, characterTable);
|
|
closeFlow();
|
|
closePage();
|
|
}
|
|
_sectionCounter++;
|
|
}
|
|
|
|
private int calculateHeaderHeight(int start, int end, int pageWidth)
|
|
{
|
|
ArrayList paragraphs = findProperties(start, end, _paragraphTable.root);
|
|
int size = paragraphs.size();
|
|
ArrayList lineHeights = new ArrayList();
|
|
//StyleContext context = StyleContext.getDefaultStyleContext();
|
|
|
|
for(int x = 0; x < size; x++)
|
|
{
|
|
PapxNode node = (PapxNode)paragraphs.get(x);
|
|
int parStart = Math.max(node.getStart(), start);
|
|
int parEnd = Math.min(node.getEnd(), end);
|
|
|
|
int lineWidth = 0;
|
|
int maxHeight = 0;
|
|
|
|
ArrayList textRuns = findProperties(parStart, parEnd, _characterTable.root);
|
|
int charSize = textRuns.size();
|
|
|
|
//StringBuffer lineBuffer = new StringBuffer();
|
|
for(int y = 0; y < charSize; y++)
|
|
{
|
|
ChpxNode charNode = (ChpxNode)textRuns.get(y);
|
|
int istd = Utils.convertBytesToShort(node.getPapx(), 0);
|
|
StyleDescription sd = _styleSheet.getStyleDescription(istd);
|
|
CHP chp = (CHP)StyleSheet.uncompressProperty(charNode.getChpx(), sd.getCHP(), _styleSheet);
|
|
|
|
//get Font info
|
|
//FontMetrics metrics = getFontMetrics(chp, context);
|
|
|
|
int height = 10;//metrics.getHeight();
|
|
maxHeight = Math.max(maxHeight, height);
|
|
|
|
int charStart = Math.max(parStart, charNode.getStart());
|
|
int charEnd = Math.min(parEnd, charNode.getEnd());
|
|
|
|
ArrayList text = findProperties(charStart, charEnd, _text.root);
|
|
|
|
int textSize = text.size();
|
|
StringBuffer buf = new StringBuffer();
|
|
for(int z = 0; z < textSize; z++)
|
|
{
|
|
|
|
TextPiece piece = (TextPiece)text.get(z);
|
|
int textStart = Math.max(piece.getStart(), charStart);
|
|
int textEnd = Math.min(piece.getEnd(), charEnd);
|
|
|
|
if(piece.usesUnicode())
|
|
{
|
|
addUnicodeText(textStart, textEnd, buf);
|
|
}
|
|
else
|
|
{
|
|
addText(textStart, textEnd, buf);
|
|
}
|
|
}
|
|
|
|
String tempString = buf.toString();
|
|
lineWidth += 10 * tempString.length();//metrics.stringWidth(tempString);
|
|
if(lineWidth > pageWidth)
|
|
{
|
|
lineHeights.add(new Integer(maxHeight));
|
|
maxHeight = 0;
|
|
lineWidth = 0;
|
|
}
|
|
}
|
|
lineHeights.add(new Integer(maxHeight));
|
|
}
|
|
int sum = 0;
|
|
size = lineHeights.size();
|
|
for(int x = 0; x < size; x++)
|
|
{
|
|
Integer height = (Integer)lineHeights.get(x);
|
|
sum += height.intValue();
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
/* private FontMetrics getFontMetrics(CHP chp, StyleContext context)
|
|
{
|
|
String fontName = _fonts.getFont(chp._ftcAscii);
|
|
int style = 0;
|
|
if(chp._bold)
|
|
{
|
|
style |= Font.BOLD;
|
|
}
|
|
if(chp._italic)
|
|
{
|
|
style |= Font.ITALIC;
|
|
}
|
|
|
|
Font font = new Font(fontName, style, chp._hps/2);
|
|
|
|
|
|
return context.getFontMetrics(font);
|
|
}*/
|
|
private String createRegion(boolean before, HeaderFooter header, SEP sep, String name)
|
|
{
|
|
if(header.isEmpty())
|
|
{
|
|
return "";
|
|
}
|
|
String region = "region-name=\"" + name + "\"";
|
|
if(name == null)
|
|
{
|
|
region = "";
|
|
}
|
|
int height = calculateHeaderHeight(header.getStart(), header.getEnd(), sep._xaPage/20);
|
|
int marginTop = 0;
|
|
int marginBottom = 0;
|
|
int extent = 0;
|
|
String where = null;
|
|
String align = null;
|
|
|
|
if(before)
|
|
{
|
|
where = "before";
|
|
align = "before";
|
|
marginTop = sep._dyaHdrTop/20;
|
|
extent = height + marginTop;
|
|
sep._dyaTop = Math.max(extent*20, sep._dyaTop);
|
|
}
|
|
else
|
|
{
|
|
where = "after";
|
|
align = "after";
|
|
marginBottom = sep._dyaHdrBottom/20;
|
|
extent = height + marginBottom;
|
|
sep._dyaBottom = Math.max(extent*20, sep._dyaBottom);
|
|
}
|
|
|
|
int marginLeft = sep._dxaLeft/20;
|
|
int marginRight = sep._dxaRight/20;
|
|
|
|
return "<fo:region-" + where + " display-align=\"" + align + "\" extent=\"" +
|
|
extent + "pt\" padding-left=\"" + marginLeft + "pt\" padding-right=\"" +
|
|
marginRight + "pt\" padding-top=\"" + marginTop + "pt\" padding-bottom=\"" +
|
|
marginBottom + "pt\" " + region + "/>";
|
|
|
|
}
|
|
private String createRegion(String where, String name)
|
|
{
|
|
return "<fo:region-" + where + " overflow=\"scroll\" region-name=\"" + name + "\"/>";
|
|
}
|
|
private String createEvenOddPageSequence(String titlePage, String evenPage, String oddPage, int counter)
|
|
{
|
|
String name = "my-sequence" + counter;
|
|
_headerBuffer.append("<fo:page-sequence-master master-name=\"" + name + "\"> ");
|
|
_headerBuffer.append("<fo:repeatable-page-master-alternatives>");
|
|
if(titlePage != null)
|
|
{
|
|
_headerBuffer.append("<fo:conditional-page-master-reference " +
|
|
"page-position=\"first\" master-reference=\"" +
|
|
titlePage + "\"/>");
|
|
}
|
|
_headerBuffer.append("<fo:conditional-page-master-reference odd-or-even=\"odd\" ");
|
|
_headerBuffer.append("master-reference=\""+ oddPage + "\"/> ");
|
|
_headerBuffer.append("<fo:conditional-page-master-reference odd-or-even=\"even\" ");
|
|
_headerBuffer.append("master-reference=\"" + evenPage + "\"/> ");
|
|
_headerBuffer.append("</fo:repeatable-page-master-alternatives>");
|
|
_headerBuffer.append("</fo:page-sequence-master>");
|
|
return name;
|
|
}
|
|
private String createPageSequence(String titlePage, String regPage, int counter)
|
|
{
|
|
String name = null;
|
|
if(titlePage != null)
|
|
{
|
|
name = "my-sequence" + counter;
|
|
_headerBuffer.append("<fo:page-sequence-master master-name=\"" + name + "\"> ");
|
|
_headerBuffer.append("<fo:single-page-master-reference master-reference=\"" + titlePage + "\"/>");
|
|
_headerBuffer.append("<fo:repeatable-page-master-reference master-reference=\"" + regPage + "\"/>");
|
|
_headerBuffer.append("</fo:page-sequence-master>");
|
|
}
|
|
return name;
|
|
}
|
|
private void addBlockContent(int start, int end, BTreeSet text,
|
|
BTreeSet paragraphTable, BTreeSet characterTable)
|
|
{
|
|
|
|
BTreeSet.BTreeNode root = paragraphTable.root;
|
|
ArrayList pars = findProperties(start, end, root);
|
|
//root = characterTable.root;
|
|
int size = pars.size();
|
|
|
|
for(int c = 0; c < size; c++)
|
|
{
|
|
PapxNode currentNode = (PapxNode)pars.get(c);
|
|
createParagraph(start, end, currentNode, characterTable, text);
|
|
}
|
|
//closePage();
|
|
}
|
|
private String getTextAlignment(byte jc)
|
|
{
|
|
switch(jc)
|
|
{
|
|
case 0:
|
|
return "start";
|
|
case 1:
|
|
return "center";
|
|
case 2:
|
|
return "end";
|
|
case 3:
|
|
return "justify";
|
|
default:
|
|
return "left";
|
|
}
|
|
}
|
|
private void createParagraph(int start, int end, PapxNode currentNode,
|
|
BTreeSet characterTable, BTreeSet text)
|
|
{
|
|
StringBuffer blockBuffer = _bodyBuffer;
|
|
byte[] papx = currentNode.getPapx();
|
|
int istd = Utils.convertBytesToShort(papx, 0);
|
|
StyleDescription std = _styleSheet.getStyleDescription(istd);
|
|
PAP pap = (PAP)StyleSheet.uncompressProperty(papx, std.getPAP(), _styleSheet);
|
|
|
|
//handle table cells
|
|
if(pap._fInTable > 0)
|
|
{
|
|
if(pap._fTtp == 0)
|
|
{
|
|
if(_cellBuffer == null)
|
|
{
|
|
_cellBuffer = new StringBuffer();
|
|
}
|
|
blockBuffer = _cellBuffer;
|
|
}
|
|
else
|
|
{
|
|
if(_table == null)
|
|
{
|
|
_table = new ArrayList();
|
|
}
|
|
TAP tap = (TAP)StyleSheet.uncompressProperty(papx, new TAP(), _styleSheet);
|
|
TableRow nextRow = new TableRow(_cells, tap);
|
|
_table.add(nextRow);
|
|
_cells = null;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//just prints out any table that is stored in _table
|
|
printTable();
|
|
}
|
|
|
|
if(pap._ilfo > 0)
|
|
{
|
|
LVL lvl = _listTables.getLevel(pap._ilfo, pap._ilvl);
|
|
addListParagraphContent(lvl, blockBuffer, pap, currentNode, start, end, std);
|
|
}
|
|
else
|
|
{
|
|
addParagraphContent(blockBuffer, pap, currentNode, start, end, std);
|
|
}
|
|
|
|
}
|
|
|
|
private void addListParagraphContent(LVL lvl, StringBuffer blockBuffer, PAP pap,
|
|
PapxNode currentNode, int start, int end,
|
|
StyleDescription std)
|
|
{
|
|
pap = (PAP)StyleSheet.uncompressProperty(lvl._papx, pap, _styleSheet, false);
|
|
|
|
addParagraphProperties(pap, blockBuffer);
|
|
|
|
ArrayList charRuns = findProperties(Math.max(currentNode.getStart(), start),
|
|
Math.min(currentNode.getEnd(), end),
|
|
_characterTable.root);
|
|
int len = charRuns.size();
|
|
|
|
CHP numChp = (CHP)StyleSheet.uncompressProperty(((ChpxNode)charRuns.get(len-1)).getChpx(), std.getCHP(), _styleSheet);
|
|
|
|
numChp = (CHP)StyleSheet.uncompressProperty(lvl._chpx, numChp, _styleSheet);
|
|
|
|
//StyleContext context = StyleContext.getDefaultStyleContext();
|
|
//FontMetrics metrics = getFontMetrics(numChp, context);
|
|
int indent = -1 * pap._dxaLeft1;
|
|
String bulletText = getBulletText(lvl, pap);
|
|
|
|
indent = indent - (bulletText.length() * 10) * 20;//(metrics.stringWidth(bulletText) * 20);
|
|
|
|
if(indent > 0)
|
|
{
|
|
numChp._paddingEnd = (short)indent;
|
|
}
|
|
|
|
addCharacterProperties(numChp, blockBuffer);
|
|
int listNum = 0;
|
|
|
|
//if(number != null)
|
|
//{
|
|
blockBuffer.append(bulletText);
|
|
//listNum = 1;
|
|
//}
|
|
|
|
//for(;listNum < lvl._xst.length; listNum++)
|
|
//{
|
|
// addText(lvl._xst[listNum], blockBuffer);
|
|
//}
|
|
|
|
|
|
switch (lvl._ixchFollow)
|
|
{
|
|
case 0:
|
|
addText('\u0009', blockBuffer);
|
|
break;
|
|
case 1:
|
|
addText(' ', blockBuffer);
|
|
break;
|
|
}
|
|
|
|
closeLine(blockBuffer);
|
|
for(int x = 0; x < len; x++)
|
|
{
|
|
ChpxNode charNode = (ChpxNode)charRuns.get(x);
|
|
byte[] chpx = charNode.getChpx();
|
|
CHP chp = (CHP)StyleSheet.uncompressProperty(chpx, std.getCHP(), _styleSheet);
|
|
|
|
|
|
addCharacterProperties(chp, blockBuffer);
|
|
|
|
int charStart = Math.max(charNode.getStart(), currentNode.getStart());
|
|
int charEnd = Math.min(charNode.getEnd(), currentNode.getEnd());
|
|
ArrayList textRuns = findProperties(charStart, charEnd, _text.root);
|
|
int textRunLen = textRuns.size();
|
|
for(int y = 0; y < textRunLen; y++)
|
|
{
|
|
TextPiece piece = (TextPiece)textRuns.get(y);
|
|
charStart = Math.max(charStart, piece.getStart());
|
|
charEnd = Math.min(charEnd, piece.getEnd());
|
|
|
|
if(piece.usesUnicode())
|
|
{
|
|
addUnicodeText(charStart, charEnd, blockBuffer);
|
|
}
|
|
else
|
|
{
|
|
addText(charStart, charEnd, blockBuffer);
|
|
}
|
|
closeLine(blockBuffer);
|
|
}
|
|
}
|
|
closeBlock(blockBuffer);
|
|
}
|
|
|
|
private void addParagraphContent(StringBuffer blockBuffer, PAP pap,
|
|
PapxNode currentNode, int start, int end,
|
|
StyleDescription std)
|
|
{
|
|
addParagraphProperties(pap, blockBuffer);
|
|
|
|
ArrayList charRuns = findProperties(Math.max(currentNode.getStart(), start),
|
|
Math.min(currentNode.getEnd(), end),
|
|
_characterTable.root);
|
|
int len = charRuns.size();
|
|
|
|
for(int x = 0; x < len; x++)
|
|
{
|
|
ChpxNode charNode = (ChpxNode)charRuns.get(x);
|
|
byte[] chpx = charNode.getChpx();
|
|
CHP chp = (CHP)StyleSheet.uncompressProperty(chpx, std.getCHP(), _styleSheet);
|
|
|
|
addCharacterProperties(chp, blockBuffer);
|
|
|
|
int charStart = Math.max(charNode.getStart(), currentNode.getStart());
|
|
int charEnd = Math.min(charNode.getEnd(), currentNode.getEnd());
|
|
ArrayList textRuns = findProperties(charStart, charEnd, _text.root);
|
|
int textRunLen = textRuns.size();
|
|
for(int y = 0; y < textRunLen; y++)
|
|
{
|
|
TextPiece piece = (TextPiece)textRuns.get(y);
|
|
charStart = Math.max(charStart, piece.getStart());
|
|
charEnd = Math.min(charEnd, piece.getEnd());
|
|
|
|
if(piece.usesUnicode())
|
|
{
|
|
addUnicodeText(charStart, charEnd, blockBuffer);
|
|
}
|
|
else
|
|
{
|
|
addText(charStart, charEnd, blockBuffer);
|
|
}
|
|
closeLine(blockBuffer);
|
|
}
|
|
}
|
|
closeBlock(blockBuffer);
|
|
}
|
|
private void addText(int start, int end, StringBuffer buf)
|
|
{
|
|
for(int x = start; x < end; x++)
|
|
{
|
|
char ch = '?';
|
|
|
|
|
|
ch = (char)_header[x];
|
|
|
|
addText(ch, buf);
|
|
}
|
|
}
|
|
private void addText(char ch, StringBuffer buf)
|
|
{
|
|
int num = 0xffff & ch;
|
|
if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
|
|
(ch >= '0' && ch <= '9') || ch == '_' || ch == ' ' || ch == '-' || ch == '.' || ch == '$')
|
|
{
|
|
buf.append(ch);
|
|
}
|
|
else if(num == 0x07 && _cellBuffer != null)
|
|
{
|
|
|
|
if(_cells == null)
|
|
{
|
|
_cells = new ArrayList();
|
|
}
|
|
closeLine(_cellBuffer);
|
|
closeBlock(_cellBuffer);
|
|
_cells.add(_cellBuffer.toString());
|
|
_cellBuffer = null;
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
/** @todo handle special characters */
|
|
if(num < 0x20)
|
|
num=0x20;
|
|
buf.append("&#");
|
|
buf.append(num);
|
|
buf.append(';');
|
|
}
|
|
}
|
|
private void addUnicodeText(int start, int end, StringBuffer buf)
|
|
{
|
|
for(int x = start; x < end; x += 2)
|
|
{
|
|
char ch = Utils.getUnicodeCharacter(_header, x);
|
|
//if(ch < 0x0020)
|
|
//{
|
|
// _bodyBuffer.append('?');
|
|
//}
|
|
//else
|
|
//{
|
|
addText(ch, buf);
|
|
//}
|
|
}
|
|
}
|
|
private void addParagraphProperties(PAP pap, StringBuffer buf)
|
|
{
|
|
buf.append("<fo:block ");
|
|
buf.append("text-align=\"" + getTextAlignment(pap._jc) + "\"\r\n");
|
|
buf.append("linefeed-treatment=\"preserve\" ");
|
|
buf.append("white-space-collapse=\"false\" ");
|
|
|
|
if(pap._fKeep > 0)
|
|
{
|
|
buf.append("keep-together.within-page=\"always\"\r\n");
|
|
}
|
|
if(pap._fKeepFollow > 0)
|
|
{
|
|
buf.append("keep-with-next.within-page=\"always\"\r\n");
|
|
}
|
|
if(pap._fPageBreakBefore > 0)
|
|
{
|
|
buf.append("break-before=\"page\"\r\n");
|
|
}
|
|
if(pap._fNoAutoHyph == 0)
|
|
{
|
|
buf.append("hyphenate=\"true\"\r\n");
|
|
}
|
|
else
|
|
{
|
|
buf.append("hyphenate=\"false\"\r\n");
|
|
}
|
|
if(pap._dxaLeft > 0)
|
|
{
|
|
buf.append("start-indent=\"" + ((float)pap._dxaLeft)/1440.0f + "in\"\r\n");
|
|
}
|
|
if(pap._dxaRight > 0)
|
|
{
|
|
buf.append("end-indent=\"" + ((float)pap._dxaRight)/1440.0f + "in\"\r\n");
|
|
}
|
|
if(pap._dxaLeft1 != 0)
|
|
{
|
|
buf.append("text-indent=\"" + ((float)pap._dxaLeft1)/1440.0f + "in\"\r\n");
|
|
}
|
|
if(pap._lspd[1] == 0)
|
|
{
|
|
//buf.append("line-height=\"" + ((float)pap._lspd[0])/1440.0f + "in\"\r\n");
|
|
}
|
|
addBorder(buf, pap._brcTop, "top");
|
|
addBorder(buf, pap._brcBottom, "bottom");
|
|
addBorder(buf, pap._brcLeft, "left");
|
|
addBorder(buf, pap._brcRight, "right");
|
|
|
|
buf.append(">");
|
|
|
|
}
|
|
|
|
private void addCharacterProperties(CHP chp, StringBuffer buf)
|
|
{
|
|
buf.append("<fo:inline ");
|
|
buf.append("font-family=\"" + _fonts.getFont(chp._ftcAscii) + "\" ");
|
|
buf.append("font-size=\"" + (chp._hps / 2) + "pt\" ");
|
|
buf.append("color=\"" + getColor(chp._ico) + "\" ");
|
|
//not supported by fop
|
|
//buf.append("letter-spacing=\"" + ((double)chp._dxaSpace)/1440.0f + "in\" ");
|
|
|
|
addBorder(buf, chp._brc, "top");
|
|
addBorder(buf, chp._brc, "bottom");
|
|
addBorder(buf, chp._brc, "left");
|
|
addBorder(buf, chp._brc, "right");
|
|
|
|
if(chp._italic)
|
|
{
|
|
buf.append("font-style=\"italic\" ");
|
|
}
|
|
if(chp._bold)
|
|
{
|
|
buf.append("font-weight=\"bold\" ");
|
|
}
|
|
if(chp._fSmallCaps)
|
|
{
|
|
buf.append("font-variant=\"small-caps\" ");
|
|
}
|
|
if(chp._fCaps)
|
|
{
|
|
buf.append("text-transform=\"uppercase\" ");
|
|
}
|
|
if(chp._fStrike || chp._fDStrike)
|
|
{
|
|
buf.append("text-decoration=\"line-through\" ");
|
|
}
|
|
if(chp._fShadow)
|
|
{
|
|
int size = chp._hps/24;
|
|
buf.append("text-shadow=\"" + size + "pt\"");
|
|
}
|
|
if(chp._fLowerCase)
|
|
{
|
|
buf.append("text-transform=\"lowercase\" ");
|
|
}
|
|
if(chp._kul > 0)
|
|
{
|
|
buf.append("text-decoration=\"underline\" ");
|
|
}
|
|
if(chp._highlighted)
|
|
{
|
|
buf.append("background-color=\"" + getColor(chp._icoHighlight) + "\" ");
|
|
}
|
|
if(chp._paddingStart != 0)
|
|
{
|
|
buf.append("padding-start=\"" + (float)chp._paddingStart/1440.0f + "in\" ");
|
|
}
|
|
if(chp._paddingEnd != 0)
|
|
{
|
|
buf.append("padding-end=\"" + (float)chp._paddingEnd/1440.0f + "in\" ");
|
|
}
|
|
buf.append(">");
|
|
}
|
|
private void addStaticContent(String flowName, HeaderFooter content)
|
|
{
|
|
_bodyBuffer.append("<fo:static-content flow-name=\"" + flowName + "\">");
|
|
//_bodyBuffer.append("<fo:float float=\"before\">");
|
|
addBlockContent(content.getStart(), content.getEnd(),_text, _paragraphTable, _characterTable);
|
|
//_bodyBuffer.append("</fo:float>");
|
|
_bodyBuffer.append("</fo:static-content>");
|
|
|
|
}
|
|
private String getBulletText(LVL lvl, PAP pap)
|
|
{
|
|
StringBuffer bulletBuffer = new StringBuffer();
|
|
for(int x = 0; x < lvl._xst.length; x++)
|
|
{
|
|
if(lvl._xst[x] < 9)
|
|
{
|
|
LVL numLevel = _listTables.getLevel(pap._ilfo, lvl._xst[x]);
|
|
int num = numLevel._iStartAt;
|
|
if(lvl == numLevel)
|
|
{
|
|
numLevel._iStartAt++;
|
|
}
|
|
else if(num > 1)
|
|
{
|
|
num--;
|
|
}
|
|
bulletBuffer.append(NumberFormatter.getNumber(num, lvl._nfc));
|
|
|
|
}
|
|
else
|
|
{
|
|
bulletBuffer.append(lvl._xst[x]);
|
|
}
|
|
|
|
}
|
|
return bulletBuffer.toString();
|
|
}
|
|
/**
|
|
* finds all chpx's that are between start and end
|
|
*/
|
|
private ArrayList findProperties(int start, int end, BTreeSet.BTreeNode root)
|
|
{
|
|
ArrayList results = new ArrayList();
|
|
BTreeSet.Entry[] entries = root.entries;
|
|
|
|
for(int x = 0; x < entries.length; x++)
|
|
{
|
|
if(entries[x] != null)
|
|
{
|
|
BTreeSet.BTreeNode child = entries[x].child;
|
|
PropertyNode xNode = (PropertyNode)entries[x].element;
|
|
if(xNode != null)
|
|
{
|
|
int xStart = xNode.getStart();
|
|
int xEnd = xNode.getEnd();
|
|
if(xStart < end)
|
|
{
|
|
if(xStart >= start)
|
|
{
|
|
if(child != null)
|
|
{
|
|
ArrayList beforeItems = findProperties(start, end, child);
|
|
results.addAll(beforeItems);
|
|
}
|
|
results.add(xNode);
|
|
}
|
|
else if(start < xEnd)
|
|
{
|
|
results.add(xNode);
|
|
//break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(child != null)
|
|
{
|
|
ArrayList beforeItems = findProperties(start, end, child);
|
|
results.addAll(beforeItems);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if(child != null)
|
|
{
|
|
ArrayList afterItems = findProperties(start, end, child);
|
|
results.addAll(afterItems);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
private void openPage(String page, String type)
|
|
{
|
|
_bodyBuffer.append("<fo:page-sequence master-reference=\"" + page + "\">\r\n");
|
|
}
|
|
private void openFlow()
|
|
{
|
|
_bodyBuffer.append("<fo:flow flow-name=\"xsl-region-body\">\r\n");
|
|
}
|
|
private void closeFlow()
|
|
{
|
|
_bodyBuffer.append("</fo:flow>\r\n");
|
|
}
|
|
private void closePage()
|
|
{
|
|
_bodyBuffer.append("</fo:page-sequence>\r\n");
|
|
}
|
|
private void closeLine(StringBuffer buf)
|
|
{
|
|
buf.append("</fo:inline>");
|
|
}
|
|
private void closeBlock(StringBuffer buf)
|
|
{
|
|
buf.append("</fo:block>\r\n");
|
|
}
|
|
private ArrayList findPAPProperties(int start, int end, BTreeSet.BTreeNode root)
|
|
{
|
|
ArrayList results = new ArrayList();
|
|
BTreeSet.Entry[] entries = root.entries;
|
|
|
|
for(int x = 0; x < entries.length; x++)
|
|
{
|
|
if(entries[x] != null)
|
|
{
|
|
BTreeSet.BTreeNode child = entries[x].child;
|
|
PapxNode papxNode = (PapxNode)entries[x].element;
|
|
if(papxNode != null)
|
|
{
|
|
int papxStart = papxNode.getStart();
|
|
if(papxStart < end)
|
|
{
|
|
if(papxStart >= start)
|
|
{
|
|
if(child != null)
|
|
{
|
|
ArrayList beforeItems = findPAPProperties(start, end, child);
|
|
results.addAll(beforeItems);
|
|
}
|
|
results.add(papxNode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(child != null)
|
|
{
|
|
ArrayList beforeItems = findPAPProperties(start, end, child);
|
|
results.addAll(beforeItems);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if(child != null)
|
|
{
|
|
ArrayList afterItems = findPAPProperties(start, end, child);
|
|
results.addAll(afterItems);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private String createPageMaster(SEP sep, String type, int section,
|
|
String regionBefore, String regionAfter)
|
|
{
|
|
float height = ((float)sep._yaPage)/1440.0f;
|
|
float width = ((float)sep._xaPage)/1440.0f;
|
|
float leftMargin = ((float)sep._dxaLeft)/1440.0f;
|
|
float rightMargin = ((float)sep._dxaRight)/1440.0f;
|
|
float topMargin = ((float)sep._dyaTop)/1440.0f;
|
|
float bottomMargin = ((float)sep._dyaBottom)/1440.0f;
|
|
|
|
//add these to the header
|
|
String thisPage = type + "-page" + section;
|
|
|
|
_headerBuffer.append("<fo:simple-page-master master-name=\"" +
|
|
thisPage + "\"\r\n");
|
|
_headerBuffer.append("page-height=\"" + height + "in\"\r\n");
|
|
_headerBuffer.append("page-width=\"" + width + "in\"\r\n");
|
|
_headerBuffer.append(">\r\n");
|
|
|
|
|
|
|
|
_headerBuffer.append("<fo:region-body ");
|
|
//top right bottom left
|
|
|
|
_headerBuffer.append("margin=\"" + topMargin + "in " + rightMargin + "in " +
|
|
bottomMargin + "in " + leftMargin + "in\"\r\n");
|
|
|
|
//String style = null;
|
|
//String color = null;
|
|
addBorder(_headerBuffer, sep._brcTop, "top");
|
|
addBorder(_headerBuffer, sep._brcBottom, "bottom");
|
|
addBorder(_headerBuffer, sep._brcLeft, "left");
|
|
addBorder(_headerBuffer, sep._brcRight, "right");
|
|
|
|
if(sep._ccolM1 > 0)
|
|
{
|
|
_headerBuffer.append("column-count=\"" + (sep._ccolM1 + 1) + "\" ");
|
|
if(sep._fEvenlySpaced)
|
|
{
|
|
_headerBuffer.append("column-gap=\"" + ((float)(sep._dxaColumns))/1440.0f + "in\"");
|
|
}
|
|
else
|
|
{
|
|
_headerBuffer.append("column-gap=\"0.25in\"");
|
|
}
|
|
}
|
|
_headerBuffer.append("/>\r\n");
|
|
|
|
if(regionBefore != null)
|
|
{
|
|
_headerBuffer.append(regionBefore);
|
|
}
|
|
if(regionAfter != null)
|
|
{
|
|
_headerBuffer.append(regionAfter);
|
|
}
|
|
|
|
_headerBuffer.append("</fo:simple-page-master>\r\n");
|
|
return thisPage;
|
|
}
|
|
private void addBorder(StringBuffer buf, short[] brc, String where)
|
|
{
|
|
if((brc[0] & 0xff00) != 0 && brc[0] != -1)
|
|
{
|
|
int type = (brc[0] & 0xff00) >> 8;
|
|
float width = ((float)(brc[0] & 0x00ff))/8.0f;
|
|
String style = getBorderStyle(brc[0]);
|
|
String color = getColor(brc[1] & 0x00ff);
|
|
String thickness = getBorderThickness(brc[0]);
|
|
buf.append("border-" + where + "-style=\"" + style + "\"\r\n");
|
|
buf.append("border-" + where + "-color=\"" + color + "\"\r\n");
|
|
buf.append("border-" + where + "-width=\"" + width + "pt\"\r\n");
|
|
}
|
|
}
|
|
public void closeDoc()
|
|
{
|
|
_headerBuffer.append("</fo:layout-master-set>");
|
|
_bodyBuffer.append("</fo:root>");
|
|
//_headerBuffer.append();
|
|
|
|
//test code
|
|
try
|
|
{
|
|
OutputStreamWriter test = new OutputStreamWriter(new FileOutputStream(_outName), "8859_1");
|
|
test.write(_headerBuffer.toString());
|
|
test.write(_bodyBuffer.toString());
|
|
test.flush();
|
|
test.close();
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
t.printStackTrace();
|
|
}
|
|
}
|
|
private String getBorderThickness(int style)
|
|
{
|
|
switch(style)
|
|
{
|
|
case 1:
|
|
return "medium";
|
|
case 2:
|
|
return "thick";
|
|
case 3:
|
|
return "medium";
|
|
case 5:
|
|
return "thin";
|
|
default:
|
|
return "medium";
|
|
}
|
|
}
|
|
|
|
|
|
private String getColor(int ico)
|
|
{
|
|
switch(ico)
|
|
{
|
|
case 1:
|
|
return "black";
|
|
case 2:
|
|
return "blue";
|
|
case 3:
|
|
return "cyan";
|
|
case 4:
|
|
return "green";
|
|
case 5:
|
|
return "magenta";
|
|
case 6:
|
|
return "red";
|
|
case 7:
|
|
return "yellow";
|
|
case 8:
|
|
return "white";
|
|
case 9:
|
|
return "darkblue";
|
|
case 10:
|
|
return "darkcyan";
|
|
case 11:
|
|
return "darkgreen";
|
|
case 12:
|
|
return "darkmagenta";
|
|
case 13:
|
|
return "darkred";
|
|
case 14:
|
|
return "darkyellow";
|
|
case 15:
|
|
return "darkgray";
|
|
case 16:
|
|
return "lightgray";
|
|
default:
|
|
return "black";
|
|
}
|
|
}
|
|
|
|
private String getBorderStyle(int type)
|
|
{
|
|
|
|
switch(type)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
return "solid";
|
|
case 3:
|
|
return "double";
|
|
case 5:
|
|
return "solid";
|
|
case 6:
|
|
return "dotted";
|
|
case 7:
|
|
case 8:
|
|
return "dashed";
|
|
case 9:
|
|
return "dotted";
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
case 16:
|
|
case 17:
|
|
case 18:
|
|
case 19:
|
|
return "double";
|
|
case 20:
|
|
return "solid";
|
|
case 21:
|
|
return "double";
|
|
case 22:
|
|
return "dashed";
|
|
case 23:
|
|
return "dashed";
|
|
case 24:
|
|
return "ridge";
|
|
case 25:
|
|
return "grooved";
|
|
default:
|
|
return "solid";
|
|
}
|
|
}
|
|
/**
|
|
* creates the List data
|
|
*
|
|
* @param tableStream Main table stream buffer.
|
|
*/
|
|
private void createListTables(byte[] tableStream)
|
|
{
|
|
|
|
|
|
int lfoOffset = LittleEndian.getInt(_header, 0x2ea);
|
|
int lfoSize = LittleEndian.getInt(_header, 0x2ee);
|
|
byte[] plflfo = new byte[lfoSize];
|
|
|
|
System.arraycopy(tableStream, lfoOffset, plflfo, 0, lfoSize);
|
|
|
|
int lstOffset = LittleEndian.getInt(_header, 0x2e2);
|
|
int lstSize = LittleEndian.getInt(_header, 0x2e2);
|
|
if(lstOffset > 0 && lstSize > 0)
|
|
{
|
|
lstSize = lfoOffset - lstOffset;
|
|
byte[] plcflst = new byte[lstSize];
|
|
System.arraycopy(tableStream, lstOffset, plcflst, 0, lstSize);
|
|
_listTables = new ListTables(plcflst, plflfo);
|
|
}
|
|
|
|
}
|
|
/**
|
|
* Creates the documents StyleSheet
|
|
*
|
|
* @param tableStream Main table stream buffer.
|
|
*
|
|
*/
|
|
private void createStyleSheet(byte[] tableStream)
|
|
{
|
|
int stshIndex = LittleEndian.getInt(_header, 0xa2);
|
|
int stshSize = LittleEndian.getInt(_header, 0xa6);
|
|
byte[] stsh = new byte[stshSize];
|
|
System.arraycopy(tableStream, stshIndex, stsh, 0, stshSize);
|
|
|
|
_styleSheet = new StyleSheet(stsh);
|
|
|
|
}
|
|
/**
|
|
* creates the Font table
|
|
*
|
|
* @param tableStream Main table stream buffer.
|
|
*/
|
|
private void createFontTable(byte[] tableStream)
|
|
{
|
|
int fontTableIndex = LittleEndian.getInt(_header, 0x112);
|
|
int fontTableSize = LittleEndian.getInt(_header, 0x116);
|
|
byte[] fontTable = new byte[fontTableSize];
|
|
System.arraycopy(tableStream, fontTableIndex, fontTable, 0, fontTableSize);
|
|
_fonts = new FontTable(fontTable);
|
|
}
|
|
|
|
|
|
private void overrideCellBorder(int row, int col, int height,
|
|
int width, TC tc, TAP tap)
|
|
{
|
|
|
|
if(row == 0)
|
|
{
|
|
if(tc._brcTop[0] == 0 || tc._brcTop[0] == -1)
|
|
{
|
|
tc._brcTop = tap._brcTop;
|
|
}
|
|
if(tc._brcBottom[0] == 0 || tc._brcBottom[0] == -1)
|
|
{
|
|
tc._brcBottom = tap._brcHorizontal;
|
|
}
|
|
}
|
|
else if(row == (height - 1))
|
|
{
|
|
if(tc._brcTop[0] == 0 || tc._brcTop[0] == -1)
|
|
{
|
|
tc._brcTop = tap._brcHorizontal;
|
|
}
|
|
if(tc._brcBottom[0] == 0 || tc._brcBottom[0] == -1)
|
|
{
|
|
tc._brcBottom = tap._brcBottom;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(tc._brcTop[0] == 0 || tc._brcTop[0] == -1)
|
|
{
|
|
tc._brcTop = tap._brcHorizontal;
|
|
}
|
|
if(tc._brcBottom[0] == 0 || tc._brcBottom[0] == -1)
|
|
{
|
|
tc._brcBottom = tap._brcHorizontal;
|
|
}
|
|
}
|
|
if(col == 0)
|
|
{
|
|
if(tc._brcLeft[0] == 0 || tc._brcLeft[0] == -1)
|
|
{
|
|
tc._brcLeft = tap._brcLeft;
|
|
}
|
|
if(tc._brcRight[0] == 0 || tc._brcRight[0] == -1)
|
|
{
|
|
tc._brcRight = tap._brcVertical;
|
|
}
|
|
}
|
|
else if(col == (width - 1))
|
|
{
|
|
if(tc._brcLeft[0] == 0 || tc._brcLeft[0] == -1)
|
|
{
|
|
tc._brcLeft = tap._brcVertical;
|
|
}
|
|
if(tc._brcRight[0] == 0 || tc._brcRight[0] == -1)
|
|
{
|
|
tc._brcRight = tap._brcRight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(tc._brcLeft[0] == 0 || tc._brcLeft[0] == -1)
|
|
{
|
|
tc._brcLeft = tap._brcVertical;
|
|
}
|
|
if(tc._brcRight[0] == 0 || tc._brcRight[0] == -1)
|
|
{
|
|
tc._brcRight = tap._brcVertical;
|
|
}
|
|
}
|
|
}
|
|
private void printTable()
|
|
{
|
|
if(_table != null)
|
|
{
|
|
int size = _table.size();
|
|
|
|
//local buffers for the table
|
|
StringBuffer tableHeaderBuffer = new StringBuffer();
|
|
StringBuffer tableBodyBuffer = new StringBuffer();
|
|
|
|
for(int x = 0; x < size; x++)
|
|
{
|
|
StringBuffer rowBuffer = tableBodyBuffer;
|
|
TableRow row = (TableRow)_table.get(x);
|
|
TAP tap = row.getTAP();
|
|
ArrayList cells = row.getCells();
|
|
|
|
if(tap._fTableHeader)
|
|
{
|
|
rowBuffer = tableHeaderBuffer;
|
|
}
|
|
rowBuffer.append("<fo:table-row ");
|
|
if(tap._dyaRowHeight > 0)
|
|
{
|
|
rowBuffer.append("height=\"" + ((float)tap._dyaRowHeight)/1440.0f + "in\" ");
|
|
}
|
|
if(tap._fCantSplit)
|
|
{
|
|
rowBuffer.append("keep-together=\"always\" ");
|
|
}
|
|
rowBuffer.append(">");
|
|
//add cells
|
|
for(int y = 0; y < tap._itcMac; y++)
|
|
{
|
|
TC tc = tap._rgtc[y];
|
|
overrideCellBorder(x, y, size, tap._itcMac, tc, tap);
|
|
rowBuffer.append("<fo:table-cell ");
|
|
rowBuffer.append("width=\"" + ((float)(tap._rgdxaCenter[y+1] - tap._rgdxaCenter[y]))/1440.0f + "in\" ");
|
|
rowBuffer.append("padding-start=\"" + ((float)tap._dxaGapHalf)/1440.0f + "in\" ");
|
|
rowBuffer.append("padding-end=\"" + ((float)tap._dxaGapHalf)/1440.0f + "in\" ");
|
|
addBorder(rowBuffer, tc._brcTop, "top");
|
|
addBorder(rowBuffer, tc._brcLeft, "left");
|
|
addBorder(rowBuffer, tc._brcBottom, "bottom");
|
|
addBorder(rowBuffer, tc._brcRight, "right");
|
|
rowBuffer.append(">");
|
|
rowBuffer.append((String)cells.get(y));
|
|
rowBuffer.append("</fo:table-cell>");
|
|
}
|
|
rowBuffer.append("</fo:table-row>");
|
|
}
|
|
StringBuffer tableBuffer = new StringBuffer();
|
|
tableBuffer.append("<fo:table>");
|
|
if(tableHeaderBuffer.length() > 0)
|
|
{
|
|
tableBuffer.append("<fo:table-header>");
|
|
tableBuffer.append(tableHeaderBuffer.toString());
|
|
tableBuffer.append("</fo:table-header>");
|
|
}
|
|
tableBuffer.append("<fo:table-body>");
|
|
tableBuffer.append(tableBodyBuffer.toString());
|
|
tableBuffer.append("</fo:table-body>");
|
|
tableBuffer.append("</fo:table>");
|
|
_bodyBuffer.append(tableBuffer.toString());
|
|
_table = null;
|
|
}
|
|
}
|
|
private void initPclfHdd(byte[] tableStream)
|
|
{
|
|
int size = Utils.convertBytesToInt(_header, 0xf6);
|
|
int pos = Utils.convertBytesToInt(_header, 0xf2);
|
|
|
|
_plcfHdd = new byte[size];
|
|
|
|
System.arraycopy(tableStream, pos, _plcfHdd, 0, size);
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|