1
2 /* ====================================================================
3 Licensed to the Apache Software Foundation (ASF) under one or more
4 contributor license agreements. See the NOTICE file distributed with
5 this work for additional information regarding copyright ownership.
6 The ASF licenses this file to You under the Apache License, Version 2.0
7 (the "License"); you may not use this file except in compliance with
8 the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 ==================================================================== */
18
19
20
21 package org.apache.poi.hdf.extractor;
22
23
24 import org.apache.poi.hdf.extractor.util;
25 import org.apache.poi.hdf.extractor.data;
26 import java.util;
27 import java.io;
28
29 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
30 import org.apache.poi.poifs.filesystem.DocumentEntry;
31
32 import org.apache.poi.util.LittleEndian;
33
34 /**
35 * This class contains the main functionality for the Word file "reader". Much
36 * of the code in this class is based on the Word 97 document file format. Only
37 * works for non-complex files
38 *
39 * @author Ryan Ackley
40 */
41
42 public class WordDocument
43 {
44 /** byte buffer containing the main Document stream*/
45 byte[] _header;
46 /** contains all style information for this document see Word 97 Doc spec*/
47 StyleSheet _styleSheet;
48 /** contains All list information for this document*/
49 ListTables _listTables;
50 /** contains global Document properties for this document*/
51 DOP _docProps = new DOP();
52
53 int _currentList = -1;
54 int _tableSize;
55 int _sectionCounter = 1;
56 /** fonts available for this document*/
57 FontTable _fonts;
58
59 /** document's text blocks*/
60 BTreeSet _text = new BTreeSet();
61 /** document's character runs */
62 BTreeSet _characterTable = new BTreeSet();
63 /** document's paragraphs*/
64 BTreeSet _paragraphTable = new BTreeSet();
65 /** doucment's sections*/
66 BTreeSet _sectionTable = new BTreeSet();
67
68 /** used for XSL-FO conversion*/
69 StringBuffer _headerBuffer = new StringBuffer();
70 /** used for XSL-FO conversion*/
71 StringBuffer _bodyBuffer = new StringBuffer();
72 /** used for XSL-FO table conversion*/
73 StringBuffer _cellBuffer;
74 /** used for XSL-FO table conversion*/
75 ArrayList _cells;
76 /** used for XSL-FO table conversion*/
77 ArrayList _table;
78
79 /** document's header and footer information*/
80 byte[] _plcfHdd;
81
82 /** starting position of text in main document stream*/
83 int _fcMin;
84 /** length of main document text stream*/
85 int _ccpText;
86 /** length of footnotes text*/
87 int _ccpFtn;
88
89 /** The name of the file to write to */
90 private static String _outName;
91
92 /** OLE stuff*/
93 private InputStream istream;
94 /** OLE stuff*/
95 private POIFSFileSystem filesystem;
96
97 //used internally
98 private static int HEADER_EVEN_INDEX = 0;
99 private static int HEADER_ODD_INDEX = 1;
100 private static int FOOTER_EVEN_INDEX = 2;
101 private static int FOOTER_ODD_INDEX = 3;
102 private static int HEADER_FIRST_INDEX = 4;
103 private static int FOOTER_FIRST_INDEX = 5;
104
105 /**
106 * right now this function takes one parameter: a Word file, and outputs an
107 * XSL-FO document at c:\test.xml (this is hardcoded)
108 */
109 public static void main(String args[])
110 {
111 /*try
112 {
113 WordDocument file = new WordDocument(args[0], "r");
114 Writer out = new BufferedWriter(new FileWriter(args[1]));
115 file.writeAllText(out);
116 out.flush();
117 out.close();
118 }
119 catch(Throwable t)
120 {
121 t.printStackTrace();
122 }*/
123 try
124 {
125 _outName = args[1];
126 WordDocument file = new WordDocument(args[0]);
127 file.closeDoc();
128 }
129 catch(Exception e)
130 {
131 e.printStackTrace();
132 }
133 System.exit(0);
134 }
135 /**
136 * Spits out the document text
137 *
138 * @param out The Writer to write the text to.
139 * @throws IOException if there is a problem while reading from the file or
140 * writing out the text.
141 */
142 public void writeAllText(Writer out) throws IOException
143 {
144 int textStart = Utils.convertBytesToInt(_header, 0x18);
145 int textEnd = Utils.convertBytesToInt(_header, 0x1c);
146 ArrayList textPieces = findProperties(textStart, textEnd, _text.root);
147 int size = textPieces.size();
148
149 for(int x = 0; x < size; x++)
150 {
151 TextPiece nextPiece = (TextPiece)textPieces.get(x);
152 int start = nextPiece.getStart();
153 int end = nextPiece.getEnd();
154 boolean unicode = nextPiece.usesUnicode();
155 int add = 1;
156
157 if(unicode)
158 {
159 add = 2;
160 char ch;
161 for(int y = start; y < end; y += add)
162 {
163 ch = (char)Utils.convertBytesToShort(_header, y);
164 out.write(ch);
165 }
166 }
167 else
168 {
169 String sText = new String(_header, start, end-start);
170 out.write(sText);
171 }
172 }
173 }
174 /**
175 * Constructs a Word document from fileName. Parses the document and places
176 * all the important stuff into data structures.
177 *
178 * @param fileName The name of the file to read.
179 * @throws IOException if there is a problem while parsing the document.
180 */
181 public WordDocument(String fileName) throws IOException
182 {
183 this(new FileInputStream(fileName));
184 }
185
186 public WordDocument(InputStream inputStream) throws IOException
187 {
188 //do Ole stuff
189 istream = inputStream;
190 filesystem = new POIFSFileSystem(istream);
191
192 //get important stuff from the Header block and parse all the
193 //data structures
194 readFIB();
195
196 //get the SEPS for the main document text
197 ArrayList sections = findProperties(_fcMin, _fcMin + _ccpText, _sectionTable.root);
198
199 //iterate through sections, paragraphs, and character runs doing what
200 //you will with the data.
201 int size = sections.size();
202 for(int x = 0; x < size; x++)
203 {
204 SepxNode node = (SepxNode)sections.get(x);
205 int start = node.getStart();
206 int end = node.getEnd();
207 SEP sep = (SEP)StyleSheet.uncompressProperty(node.getSepx(), new SEP(), _styleSheet);
208 writeSection(Math.max(_fcMin, start), Math.min(_fcMin + _ccpText, end), sep, _text, _paragraphTable, _characterTable, _styleSheet);
209 }
210 //finish
211 istream.close();
212
213 }
214 /**
215 * Extracts the main document stream from the POI file then hands off to other
216 * functions that parse other areas.
217 *
218 * @throws IOException
219 */
220 private void readFIB() throws IOException
221 {
222 //get the main document stream
223 DocumentEntry headerProps =
224 (DocumentEntry)filesystem.getRoot().getEntry("WordDocument");
225
226 //I call it the header but its also the main document stream
227 _header = new byte[headerProps.getSize()];
228 filesystem.createDocumentInputStream("WordDocument").read(_header);
229
230 //Get the information we need from the header
231 int info = LittleEndian.getShort(_header, 0xa);
232
233 _fcMin = LittleEndian.getInt(_header, 0x18);
234 _ccpText = LittleEndian.getInt(_header, 0x4c);
235 _ccpFtn = LittleEndian.getInt(_header, 0x50);
236
237 int charPLC = LittleEndian.getInt(_header, 0xfa);
238 int charPlcSize = LittleEndian.getInt(_header, 0xfe);
239 int parPLC = LittleEndian.getInt(_header, 0x102);
240 int parPlcSize = LittleEndian.getInt(_header, 0x106);
241 boolean useTable1 = (info & 0x200) != 0;
242
243 //process the text and formatting properties
244 processComplexFile(useTable1, charPLC, charPlcSize, parPLC, parPlcSize);
245 }
246
247 /**
248 * Extracts the correct Table stream from the POI filesystem then hands off to
249 * other functions to process text and formatting info. the name is based on
250 * the fact that in Word 8(97) all text (not character or paragraph formatting)
251 * is stored in complex format.
252 *
253 * @param useTable1 boolean that specifies if we should use table1 or table0
254 * @param charTable offset in table stream of character property bin table
255 * @param charPlcSize size of character property bin table
256 * @param parTable offset in table stream of paragraph property bin table.
257 * @param parPlcSize size of paragraph property bin table.
258 * @return boolean indocating success of
259 * @throws IOException
260 */
261 private void processComplexFile(boolean useTable1, int charTable,
262 int charPlcSize, int parTable, int parPlcSize) throws IOException
263 {
264
265 //get the location of the piece table
266 int complexOffset = LittleEndian.getInt(_header, 0x1a2);
267
268 String tablename=null;
269 DocumentEntry tableEntry = null;
270 if(useTable1)
271 {
272 tablename="1Table";
273 }
274 else
275 {
276 tablename="0Table";
277 }
278 tableEntry = (DocumentEntry)filesystem.getRoot().getEntry(tablename);
279
280 //load the table stream into a buffer
281 int size = tableEntry.getSize();
282 byte[] tableStream = new byte[size];
283 filesystem.createDocumentInputStream(tablename).read(tableStream);
284
285 //init the DOP for this document
286 initDocProperties(tableStream);
287 //load the header/footer raw data for this document
288 initPclfHdd(tableStream);
289 //parse out the text locations
290 findText(tableStream, complexOffset);
291 //parse out text formatting
292 findFormatting(tableStream, charTable, charPlcSize, parTable, parPlcSize);
293
294 }
295 /**
296 * Goes through the piece table and parses out the info regarding the text
297 * blocks. For Word 97 and greater all text is stored in the "complex" way
298 * because of unicode.
299 *
300 * @param tableStream buffer containing the main table stream.
301 * @param beginning of the complex data.
302 * @throws IOException
303 */
304 private void findText(byte[] tableStream, int complexOffset) throws IOException
305 {
306 //actual text
307 int pos = complexOffset;
308 //skips through the prms before we reach the piece table. These contain data
309 //for actual fast saved files
310 while(tableStream[pos] == 1)
311 {
312 pos++;
313 int skip = LittleEndian.getShort(tableStream, pos);
314 pos += 2 + skip;
315 }
316 if(tableStream[pos] != 2)
317 {
318 throw new IOException("corrupted Word file");
319 }
320 else
321 {
322 //parse out the text pieces
323 int pieceTableSize = LittleEndian.getInt(tableStream, ++pos);
324 pos += 4;
325 int pieces = (pieceTableSize - 4) / 12;
326 for (int x = 0; x < pieces; x++)
327 {
328 int filePos = LittleEndian.getInt(tableStream, pos + ((pieces + 1) * 4) + (x * 8) + 2);
329 boolean unicode = false;
330 if ((filePos & 0x40000000) == 0)
331 {
332 unicode = true;
333 }
334 else
335 {
336 unicode = false;
337 filePos &= ~(0x40000000);//gives me FC in doc stream
338 filePos /= 2;
339 }
340 int totLength = LittleEndian.getInt(tableStream, pos + (x + 1) * 4) -
341 LittleEndian.getInt(tableStream, pos + (x * 4));
342
343 TextPiece piece = new TextPiece(filePos, totLength, unicode);
344 _text.add(piece);
345
346 }
347
348 }
349 }
350
351 /**
352 * Does all of the formatting parsing
353 *
354 * @param tableStream Main table stream buffer.
355 * @param charOffset beginning of the character bin table.
356 * @param chrPlcSize size of the char bin table.
357 * @param parOffset offset of the paragraph bin table.
358 * @param size of the paragraph bin table.
359 */
360 private void findFormatting(byte[] tableStream, int charOffset,
361 int charPlcSize, int parOffset, int parPlcSize) throws IOException
362 {
363 openDoc();
364 createStyleSheet(tableStream);
365 createListTables(tableStream);
366 createFontTable(tableStream);
367
368 //find character runs
369 //Get all the chpx info and store it
370
371 int arraySize = (charPlcSize - 4)/8;
372
373 //first we must go through the bin table and find the fkps
374 for(int x = 0; x < arraySize; x++)
375 {
376
377
378 //get page number(has nothing to do with document page)
379 //containing the chpx for the paragraph
380 int PN = LittleEndian.getInt(tableStream, charOffset + (4 * (arraySize + 1) + (4 * x)));
381
382 byte[] fkp = new byte[512];
383 System.arraycopy(_header, (PN * 512), fkp, 0, 512);
384 //take each fkp and get the chpxs
385 int crun = Utils.convertUnsignedByteToInt(fkp[511]);
386 for(int y = 0; y < crun; y++)
387 {
388 //get the beginning fc of each paragraph text run
389 int fcStart = LittleEndian.getInt(fkp, y * 4);
390 int fcEnd = LittleEndian.getInt(fkp, (y+1) * 4);
391 //get the offset in fkp of the papx for this paragraph
392 int chpxOffset = 2 * Utils.convertUnsignedByteToInt(fkp[((crun + 1) * 4) + y]);
393
394 //optimization if offset == 0 use "Normal" style
395 if(chpxOffset == 0)
396
397 {
398 _characterTable.add(new ChpxNode(fcStart, fcEnd, new byte[0]));
399 continue;
400 }
401
402 int size = Utils.convertUnsignedByteToInt(fkp[chpxOffset]);
403
404 byte[] chpx = new byte[size];
405 System.arraycopy(fkp, ++chpxOffset, chpx, 0, size);
406 //_papTable.put(new Integer(fcStart), papx);
407 _characterTable.add(new ChpxNode(fcStart, fcEnd, chpx));
408 }
409
410 }
411
412 //find paragraphs
413 arraySize = (parPlcSize - 4)/8;
414 //first we must go through the bin table and find the fkps
415 for(int x = 0; x < arraySize; x++)
416 {
417 int PN = LittleEndian.getInt(tableStream, parOffset + (4 * (arraySize + 1) + (4 * x)));
418
419 byte[] fkp = new byte[512];
420 System.arraycopy(_header, (PN * 512), fkp, 0, 512);
421 //take each fkp and get the paps
422 int crun = Utils.convertUnsignedByteToInt(fkp[511]);
423 for(int y = 0; y < crun; y++)
424 {
425 //get the beginning fc of each paragraph text run
426 int fcStart = LittleEndian.getInt(fkp, y * 4);
427 int fcEnd = LittleEndian.getInt(fkp, (y+1) * 4);
428 //get the offset in fkp of the papx for this paragraph
429 int papxOffset = 2 * Utils.convertUnsignedByteToInt(fkp[((crun + 1) * 4) + (y * 13)]);
430 int size = 2 * Utils.convertUnsignedByteToInt(fkp[papxOffset]);
431 if(size == 0)
432 {
433 size = 2 * Utils.convertUnsignedByteToInt(fkp[++papxOffset]);
434 }
435 else
436 {
437 size--;
438 }
439
440 byte[] papx = new byte[size];
441 System.arraycopy(fkp, ++papxOffset, papx, 0, size);
442 _paragraphTable.add(new PapxNode(fcStart, fcEnd, papx));
443
444 }
445
446 }
447
448 //find sections
449 int fcMin = Utils.convertBytesToInt(_header, 0x18);
450 int plcfsedFC = Utils.convertBytesToInt(_header, 0xca);
451 int plcfsedSize = Utils.convertBytesToInt(_header, 0xce);
452 byte[] plcfsed = new byte[plcfsedSize];
453 System.arraycopy(tableStream, plcfsedFC, plcfsed, 0, plcfsedSize);
454
455 arraySize = (plcfsedSize - 4)/16;
456
457 //openDoc();
458
459 for(int x = 0; x < arraySize; x++)
460 {
461 int sectionStart = Utils.convertBytesToInt(plcfsed, x * 4) + fcMin;
462 int sectionEnd = Utils.convertBytesToInt(plcfsed, (x+1) * 4) + fcMin;
463 int sepxStart = Utils.convertBytesToInt(plcfsed, 4 * (arraySize + 1) + (x * 12) + 2);
464 int sepxSize = Utils.convertBytesToShort(_header, sepxStart);
465 byte[] sepx = new byte[sepxSize];
466 System.arraycopy(_header, sepxStart + 2, sepx, 0, sepxSize);
467 SepxNode node = new SepxNode(x + 1, sectionStart, sectionEnd, sepx);
468 _sectionTable.add(node);
469 }
470
471
472 }
473
474 public void openDoc()
475 {
476 _headerBuffer.append("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\r\n");
477 _headerBuffer.append("<fo:root xmlns:fo=\"http://www.w3.org/1999/XSL/Format\">\r\n");
478 _headerBuffer.append("<fo:layout-master-set>\r\n");
479
480 }
481 private HeaderFooter findSectionHdrFtr(int type, int index)
482 {
483 if(_plcfHdd.length < 50)
484 {
485 return new HeaderFooter(0,0,0);
486 }
487 int start = _fcMin + _ccpText + _ccpFtn;
488 int end = start;
489 int arrayIndex = 0;
490
491 switch(type)
492 {
493 case HeaderFooter.HEADER_EVEN:
494 arrayIndex = (HEADER_EVEN_INDEX + (index * 6));
495 break;
496 case HeaderFooter.FOOTER_EVEN:
497 arrayIndex = (FOOTER_EVEN_INDEX + (index * 6));
498 break;
499 case HeaderFooter.HEADER_ODD:
500 arrayIndex = (HEADER_ODD_INDEX + (index * 6));
501 break;
502 case HeaderFooter.FOOTER_ODD:
503 arrayIndex = (FOOTER_ODD_INDEX + (index * 6));
504 break;
505 case HeaderFooter.HEADER_FIRST:
506 arrayIndex = (HEADER_FIRST_INDEX + (index * 6));
507 break;
508 case HeaderFooter.FOOTER_FIRST:
509 arrayIndex = (FOOTER_FIRST_INDEX + (index * 6));
510 break;
511 }
512 start += Utils.convertBytesToInt(_plcfHdd, (arrayIndex * 4));
513 end += Utils.convertBytesToInt(_plcfHdd, (arrayIndex + 1) * 4);
514
515 HeaderFooter retValue = new HeaderFooter(type, start, end);
516
517 if((end - start) == 0 && index > 1)
518 {
519 retValue = findSectionHdrFtr(type, index - 1);
520 }
521 return retValue;
522 }
523 /**
524 * inits this document DOP structure.
525 *
526 * @param tableStream The documents table stream.
527 */
528 private void initDocProperties(byte[] tableStream)
529 {
530 int pos = LittleEndian.getInt(_header, 0x192);
531 int size = LittleEndian.getInt(_header, 0x196);
532 byte[] dop = new byte[size];
533
534 System.arraycopy(tableStream, pos, dop, 0, size);
535
536 _docProps._fFacingPages = (dop[0] & 0x1) > 0;
537 _docProps._fpc = (dop[0] & 0x60) >> 5;
538
539 short num = LittleEndian.getShort(dop, 2);
540 _docProps._rncFtn = (num & 0x3);
541 _docProps._nFtn = (short)(num & 0xfffc) >> 2;
542 num = LittleEndian.getShort(dop, 52);
543 _docProps._rncEdn = num & 0x3;
544 _docProps._nEdn = (short)(num & 0xfffc) >> 2;
545 num = LittleEndian.getShort(dop, 54);
546 _docProps._epc = num & 0x3;
547 }
548
549 public void writeSection(int start, int end, SEP sep, BTreeSet text,
550 BTreeSet paragraphTable, BTreeSet characterTable,
551 StyleSheet stylesheet)
552 {
553
554 HeaderFooter titleHeader = findSectionHdrFtr(HeaderFooter.HEADER_FIRST, _sectionCounter);
555 HeaderFooter titleFooter = findSectionHdrFtr(HeaderFooter.FOOTER_FIRST, _sectionCounter);
556 HeaderFooter oddHeader = findSectionHdrFtr(HeaderFooter.HEADER_ODD, _sectionCounter);
557 HeaderFooter evenHeader = findSectionHdrFtr(HeaderFooter.HEADER_EVEN, _sectionCounter);
558 HeaderFooter oddFooter = findSectionHdrFtr(HeaderFooter.FOOTER_ODD, _sectionCounter);
559 HeaderFooter evenFooter = findSectionHdrFtr(HeaderFooter.FOOTER_EVEN, _sectionCounter);
560
561 String titlePage = null;
562 String evenPage = null;
563 String oddPage = null;
564 String regPage = null;
565
566 String sequenceName = null;
567
568 /*if(sep._fTitlePage)
569 {
570 titlePage = createPageMaster(sep, "first", _sectionCounter, createRegion("before", "title-header"), createRegion("after", "title-footer"));
571
572 if(!titleHeader.isEmpty())
573 {
574 addStaticContent("title-header" + _sectionCounter, titleHeader);
575 }
576 if(!titleFooter.isEmpty())
577 {
578 addStaticContent("title-footer" + _sectionCounter, titleFooter);
579 }
580 }*/
581
582 if(_docProps._fFacingPages)
583 {
584 if(sep._fTitlePage)
585 {
586 String before = createRegion(true, titleHeader, sep, "title-header" + _sectionCounter);
587 String after = createRegion(false, titleFooter, sep, "title-footer" + _sectionCounter);
588 titlePage = createPageMaster(sep, "first", _sectionCounter, before, after);
589 }
590 String before = createRegion(true, evenHeader, sep, "even-header" + _sectionCounter);
591 String after = createRegion(false, evenFooter, sep, "even-footer" + _sectionCounter);
592 evenPage = createPageMaster(sep, "even", _sectionCounter, before, after);
593 before = createRegion(true, oddHeader, sep, "odd-header" + _sectionCounter);
594 after = createRegion(false, oddFooter, sep, "odd-footer" + _sectionCounter);
595 oddPage = createPageMaster(sep, "odd", _sectionCounter, before, after);
596 sequenceName = createEvenOddPageSequence(titlePage, evenPage, oddPage, _sectionCounter);
597
598 openPage(sequenceName, "reference");
599
600 if(sep._fTitlePage)
601 {
602
603
604 if(!titleHeader.isEmpty())
605 {
606 addStaticContent("title-header" + _sectionCounter, titleHeader);
607 }
608 if(!titleFooter.isEmpty())
609 {
610 addStaticContent("title-footer" + _sectionCounter, titleFooter);
611 }
612 }
613
614 //handle the headers and footers for odd and even pages
615 if(!oddHeader.isEmpty())
616 {
617 addStaticContent("odd-header" + _sectionCounter, oddHeader);
618 }
619 if(!oddFooter.isEmpty())
620 {
621 addStaticContent("odd-footer" + _sectionCounter, oddFooter);
622 }
623 if(!evenHeader.isEmpty())
624 {
625 addStaticContent("even-header" + _sectionCounter, evenHeader);
626 }
627 if(!evenFooter.isEmpty())
628 {
629 addStaticContent("even-footer" + _sectionCounter, evenFooter);
630 }
631 openFlow();
632 addBlockContent(start, end, text, paragraphTable, characterTable);
633 closeFlow();
634 closePage();
635 }
636 else
637 {
638 /*if(sep._fTitlePage)
639 {
640 String before = createRegion(true, titleHeader, sep);
641 String after = createRegion(false, titleFooter, sep);
642 titlePage = createPageMaster(sep, "first", _sectionCounter, before, after);
643 }*/
644 String before = createRegion(true, oddHeader, sep, null);
645 String after = createRegion(false, oddFooter, sep, null);
646 regPage = createPageMaster(sep, "page", _sectionCounter, before, after);
647
648 if(sep._fTitlePage)
649 {
650 before = createRegion(true, titleHeader, sep, "title-header" + _sectionCounter);
651 after = createRegion(false, titleFooter, sep, "title-footer" + _sectionCounter);
652 titlePage = createPageMaster(sep, "first", _sectionCounter, before, after);
653 sequenceName = createPageSequence(titlePage, regPage, _sectionCounter);
654 openPage(sequenceName, "reference");
655
656 if(!titleHeader.isEmpty())
657 {
658 addStaticContent("title-header" + _sectionCounter, titleHeader);
659 }
660 if(!titleFooter.isEmpty())
661 {
662 addStaticContent("title-footer" + _sectionCounter, titleFooter);
663 }
664 }
665 else
666 {
667 openPage(regPage, "name");
668 }
669 if(!oddHeader.isEmpty())
670 {
671 addStaticContent("xsl-region-before", oddHeader);
672 }
673 if(!oddFooter.isEmpty())
674 {
675 addStaticContent("xsl-region-after", oddFooter);
676 }
677 openFlow();
678 addBlockContent(start, end, text, paragraphTable, characterTable);
679 closeFlow();
680 closePage();
681 }
682 _sectionCounter++;
683 }
684
685 private int calculateHeaderHeight(int start, int end, int pageWidth)
686 {
687 ArrayList paragraphs = findProperties(start, end, _paragraphTable.root);
688 int size = paragraphs.size();
689 ArrayList lineHeights = new ArrayList();
690 //StyleContext context = StyleContext.getDefaultStyleContext();
691
692 for(int x = 0; x < size; x++)
693 {
694 PapxNode node = (PapxNode)paragraphs.get(x);
695 int parStart = Math.max(node.getStart(), start);
696 int parEnd = Math.min(node.getEnd(), end);
697
698 int lineWidth = 0;
699 int maxHeight = 0;
700
701 ArrayList textRuns = findProperties(parStart, parEnd, _characterTable.root);
702 int charSize = textRuns.size();
703
704 //StringBuffer lineBuffer = new StringBuffer();
705 for(int y = 0; y < charSize; y++)
706 {
707 ChpxNode charNode = (ChpxNode)textRuns.get(y);
708 int istd = Utils.convertBytesToShort(node.getPapx(), 0);
709 StyleDescription sd = _styleSheet.getStyleDescription(istd);
710 CHP chp = (CHP)StyleSheet.uncompressProperty(charNode.getChpx(), sd.getCHP(), _styleSheet);
711
712 //get Font info
713 //FontMetrics metrics = getFontMetrics(chp, context);
714
715 int height = 10;//metrics.getHeight();
716 maxHeight = Math.max(maxHeight, height);
717
718 int charStart = Math.max(parStart, charNode.getStart());
719 int charEnd = Math.min(parEnd, charNode.getEnd());
720
721 ArrayList text = findProperties(charStart, charEnd, _text.root);
722
723 int textSize = text.size();
724 StringBuffer buf = new StringBuffer();
725 for(int z = 0; z < textSize; z++)
726 {
727
728 TextPiece piece = (TextPiece)text.get(z);
729 int textStart = Math.max(piece.getStart(), charStart);
730 int textEnd = Math.min(piece.getEnd(), charEnd);
731
732 if(piece.usesUnicode())
733 {
734 addUnicodeText(textStart, textEnd, buf);
735 }
736 else
737 {
738 addText(textStart, textEnd, buf);
739 }
740 }
741
742 String tempString = buf.toString();
743 lineWidth += 10 * tempString.length();//metrics.stringWidth(tempString);
744 if(lineWidth > pageWidth)
745 {
746 lineHeights.add(new Integer(maxHeight));
747 maxHeight = 0;
748 lineWidth = 0;
749 }
750 }
751 lineHeights.add(new Integer(maxHeight));
752 }
753 int sum = 0;
754 size = lineHeights.size();
755 for(int x = 0; x < size; x++)
756 {
757 Integer height = (Integer)lineHeights.get(x);
758 sum += height.intValue();
759 }
760
761 return sum;
762 }
763 /* private FontMetrics getFontMetrics(CHP chp, StyleContext context)
764 {
765 String fontName = _fonts.getFont(chp._ftcAscii);
766 int style = 0;
767 if(chp._bold)
768 {
769 style |= Font.BOLD;
770 }
771 if(chp._italic)
772 {
773 style |= Font.ITALIC;
774 }
775
776 Font font = new Font(fontName, style, chp._hps/2);
777
778
779 return context.getFontMetrics(font);
780 }*/
781 private String createRegion(boolean before, HeaderFooter header, SEP sep, String name)
782 {
783 if(header.isEmpty())
784 {
785 return "";
786 }
787 String region = "region-name=\"" + name + "\"";
788 if(name == null)
789 {
790 region = "";
791 }
792 int height = calculateHeaderHeight(header.getStart(), header.getEnd(), sep._xaPage/20);
793 int marginTop = 0;
794 int marginBottom = 0;
795 int extent = 0;
796 String where = null;
797 String align = null;
798
799 if(before)
800 {
801 where = "before";
802 align = "before";
803 marginTop = sep._dyaHdrTop/20;
804 extent = height + marginTop;
805 sep._dyaTop = Math.max(extent*20, sep._dyaTop);
806 }
807 else
808 {
809 where = "after";
810 align = "after";
811 marginBottom = sep._dyaHdrBottom/20;
812 extent = height + marginBottom;
813 sep._dyaBottom = Math.max(extent*20, sep._dyaBottom);
814 }
815
816 int marginLeft = sep._dxaLeft/20;
817 int marginRight = sep._dxaRight/20;
818
819 return "<fo:region-" + where + " display-align=\"" + align + "\" extent=\""
820 + extent + "pt\" "+region+"/>";
821 // org.apache.fop.fo.expr.PropertyException:
822 // Border and padding for region "xsl-region-before" must be '0'
823 // (See 6.4.13 in XSL 1.0).
824 // extent + "pt\" padding-left=\"" + marginLeft + "pt\" padding-right=\"" +
825 // marginRight + "pt\" padding-top=\"" + marginTop + "pt\" padding-bottom=\"" +
826 // marginBottom + "pt\" " + region + "/>";
827
828 }
829 private String createRegion(String where, String name)
830 {
831 return "<fo:region-" + where + " overflow=\"scroll\" region-name=\"" + name + "\"/>";
832 }
833 private String createEvenOddPageSequence(String titlePage, String evenPage, String oddPage, int counter)
834 {
835 String name = "my-sequence" + counter;
836 _headerBuffer.append("<fo:page-sequence-master master-name=\"" + name + "\"> ");
837 _headerBuffer.append("<fo:repeatable-page-master-alternatives>");
838 if(titlePage != null)
839 {
840 _headerBuffer.append("<fo:conditional-page-master-reference " +
841 "page-position=\"first\" master-reference=\"" +
842 titlePage + "\"/>");
843 }
844 _headerBuffer.append("<fo:conditional-page-master-reference odd-or-even=\"odd\" ");
845 _headerBuffer.append("master-reference=\""+ oddPage + "\"/> ");
846 _headerBuffer.append("<fo:conditional-page-master-reference odd-or-even=\"even\" ");
847 _headerBuffer.append("master-reference=\"" + evenPage + "\"/> ");
848 _headerBuffer.append("</fo:repeatable-page-master-alternatives>");
849 _headerBuffer.append("</fo:page-sequence-master>");
850 return name;
851 }
852 private String createPageSequence(String titlePage, String regPage, int counter)
853 {
854 String name = null;
855 if(titlePage != null)
856 {
857 name = "my-sequence" + counter;
858 _headerBuffer.append("<fo:page-sequence-master master-name=\"" + name + "\"> ");
859 _headerBuffer.append("<fo:single-page-master-reference master-reference=\"" + titlePage + "\"/>");
860 _headerBuffer.append("<fo:repeatable-page-master-reference master-reference=\"" + regPage + "\"/>");
861 _headerBuffer.append("</fo:page-sequence-master>");
862 }
863 return name;
864 }
865 private void addBlockContent(int start, int end, BTreeSet text,
866 BTreeSet paragraphTable, BTreeSet characterTable)
867 {
868
869 BTreeSet.BTreeNode root = paragraphTable.root;
870 ArrayList pars = findProperties(start, end, root);
871 //root = characterTable.root;
872 int size = pars.size();
873
874 for(int c = 0; c < size; c++)
875 {
876 PapxNode currentNode = (PapxNode)pars.get(c);
877 createParagraph(start, end, currentNode, characterTable, text);
878 }
879 //closePage();
880 }
881 private String getTextAlignment(byte jc)
882 {
883 switch(jc)
884 {
885 case 0:
886 return "start";
887 case 1:
888 return "center";
889 case 2:
890 return "end";
891 case 3:
892 return "justify";
893 default:
894 return "left";
895 }
896 }
897 private void createParagraph(int start, int end, PapxNode currentNode,
898 BTreeSet characterTable, BTreeSet text)
899 {
900 StringBuffer blockBuffer = _bodyBuffer;
901 byte[] papx = currentNode.getPapx();
902 int istd = Utils.convertBytesToShort(papx, 0);
903 StyleDescription std = _styleSheet.getStyleDescription(istd);
904 PAP pap = (PAP)StyleSheet.uncompressProperty(papx, std.getPAP(), _styleSheet);
905
906 //handle table cells
907 if(pap._fInTable > 0)
908 {
909 if(pap._fTtp == 0)
910 {
911 if(_cellBuffer == null)
912 {
913 _cellBuffer = new StringBuffer();
914 }
915 blockBuffer = _cellBuffer;
916 }
917 else
918 {
919 if(_table == null)
920 {
921 _table = new ArrayList();
922 }
923 TAP tap = (TAP)StyleSheet.uncompressProperty(papx, new TAP(), _styleSheet);
924 TableRow nextRow = new TableRow(_cells, tap);
925 _table.add(nextRow);
926 _cells = null;
927 return;
928 }
929 }
930 else
931 {
932 //just prints out any table that is stored in _table
933 printTable();
934 }
935
936 if(pap._ilfo > 0)
937 {
938 LVL lvl = _listTables.getLevel(pap._ilfo, pap._ilvl);
939 addListParagraphContent(lvl, blockBuffer, pap, currentNode, start, end, std);
940 }
941 else
942 {
943 addParagraphContent(blockBuffer, pap, currentNode, start, end, std);
944 }
945
946 }
947
948 private void addListParagraphContent(LVL lvl, StringBuffer blockBuffer, PAP pap,
949 PapxNode currentNode, int start, int end,
950 StyleDescription std)
951 {
952 pap = (PAP)StyleSheet.uncompressProperty(lvl._papx, pap, _styleSheet, false);
953
954 addParagraphProperties(pap, blockBuffer);
955
956 ArrayList charRuns = findProperties(Math.max(currentNode.getStart(), start),
957 Math.min(currentNode.getEnd(), end),
958 _characterTable.root);
959 int len = charRuns.size();
960
961 CHP numChp = (CHP)StyleSheet.uncompressProperty(((ChpxNode)charRuns.get(len-1)).getChpx(), std.getCHP(), _styleSheet);
962
963 numChp = (CHP)StyleSheet.uncompressProperty(lvl._chpx, numChp, _styleSheet);
964
965 //StyleContext context = StyleContext.getDefaultStyleContext();
966 //FontMetrics metrics = getFontMetrics(numChp, context);
967 int indent = -1 * pap._dxaLeft1;
968 String bulletText = getBulletText(lvl, pap);
969
970 indent = indent - (bulletText.length() * 10) * 20;//(metrics.stringWidth(bulletText) * 20);
971
972 if(indent > 0)
973 {
974 numChp._paddingEnd = (short)indent;
975 }
976
977 addCharacterProperties(numChp, blockBuffer);
978 int listNum = 0;
979
980 //if(number != null)
981 //{
982 blockBuffer.append(bulletText);
983 //listNum = 1;
984 //}
985
986 //for(;listNum < lvl._xst.length; listNum++)
987 //{
988 // addText(lvl._xst[listNum], blockBuffer);
989 //}
990
991
992 switch (lvl._ixchFollow)
993 {
994 case 0:
995 addText('\u0009', blockBuffer);
996 break;
997 case 1:
998 addText(' ', blockBuffer);
999 break;
1000 }
1001
1002 closeLine(blockBuffer);
1003 for(int x = 0; x < len; x++)
1004 {
1005 ChpxNode charNode = (ChpxNode)charRuns.get(x);
1006 byte[] chpx = charNode.getChpx();
1007 CHP chp = (CHP)StyleSheet.uncompressProperty(chpx, std.getCHP(), _styleSheet);
1008
1009
1010 addCharacterProperties(chp, blockBuffer);
1011
1012 int charStart = Math.max(charNode.getStart(), currentNode.getStart());
1013 int charEnd = Math.min(charNode.getEnd(), currentNode.getEnd());
1014 ArrayList textRuns = findProperties(charStart, charEnd, _text.root);
1015 int textRunLen = textRuns.size();
1016 for(int y = 0; y < textRunLen; y++)
1017 {
1018 TextPiece piece = (TextPiece)textRuns.get(y);
1019 charStart = Math.max(charStart, piece.getStart());
1020 charEnd = Math.min(charEnd, piece.getEnd());
1021
1022 if(piece.usesUnicode())
1023 {
1024 addUnicodeText(charStart, charEnd, blockBuffer);
1025 }
1026 else
1027 {
1028 addText(charStart, charEnd, blockBuffer);
1029 }
1030 closeLine(blockBuffer);
1031 }
1032 }
1033 closeBlock(blockBuffer);
1034 }
1035
1036 private void addParagraphContent(StringBuffer blockBuffer, PAP pap,
1037 PapxNode currentNode, int start, int end,
1038 StyleDescription std)
1039 {
1040 addParagraphProperties(pap, blockBuffer);
1041
1042 ArrayList charRuns = findProperties(Math.max(currentNode.getStart(), start),
1043 Math.min(currentNode.getEnd(), end),
1044 _characterTable.root);
1045 int len = charRuns.size();
1046
1047 for(int x = 0; x < len; x++)
1048 {
1049 ChpxNode charNode = (ChpxNode)charRuns.get(x);
1050 byte[] chpx = charNode.getChpx();
1051 CHP chp = (CHP)StyleSheet.uncompressProperty(chpx, std.getCHP(), _styleSheet);
1052
1053 addCharacterProperties(chp, blockBuffer);
1054
1055 int charStart = Math.max(charNode.getStart(), currentNode.getStart());
1056 int charEnd = Math.min(charNode.getEnd(), currentNode.getEnd());
1057 ArrayList textRuns = findProperties(charStart, charEnd, _text.root);
1058 int textRunLen = textRuns.size();
1059 for(int y = 0; y < textRunLen; y++)
1060 {
1061 TextPiece piece = (TextPiece)textRuns.get(y);
1062 charStart = Math.max(charStart, piece.getStart());
1063 charEnd = Math.min(charEnd, piece.getEnd());
1064
1065 if(piece.usesUnicode())
1066 {
1067 addUnicodeText(charStart, charEnd, blockBuffer);
1068 }
1069 else
1070 {
1071 addText(charStart, charEnd, blockBuffer);
1072 }
1073 closeLine(blockBuffer);
1074 }
1075 }
1076 closeBlock(blockBuffer);
1077 }
1078 private void addText(int start, int end, StringBuffer buf)
1079 {
1080 for(int x = start; x < end; x++)
1081 {
1082 char ch = '?';
1083
1084
1085 ch = (char)_header[x];
1086
1087 addText(ch, buf);
1088 }
1089 }
1090 private void addText(char ch, StringBuffer buf)
1091 {
1092 int num = 0xffff & ch;
1093 if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
1094 (ch >= '0' && ch <= '9') || ch == '_' || ch == ' ' || ch == '-' || ch == '.' || ch == '$')
1095 {
1096 buf.append(ch);
1097 }
1098 else if(num == 0x07 && _cellBuffer != null)
1099 {
1100
1101 if(_cells == null)
1102 {
1103 _cells = new ArrayList();
1104 }
1105 closeLine(_cellBuffer);
1106 closeBlock(_cellBuffer);
1107 _cells.add(_cellBuffer.toString());
1108 _cellBuffer = null;
1109
1110 }
1111
1112 else
1113 {
1114 /** @todo handle special characters */
1115 if(num < 0x20)
1116 num=0x20;
1117 buf.append("&#");
1118 buf.append(num);
1119 buf.append(';');
1120 }
1121 }
1122 private void addUnicodeText(int start, int end, StringBuffer buf)
1123 {
1124 for(int x = start; x < end; x += 2)
1125 {
1126 char ch = Utils.getUnicodeCharacter(_header, x);
1127 //if(ch < 0x0020)
1128 //{
1129 // _bodyBuffer.append('?');
1130 //}
1131 //else
1132 //{
1133 addText(ch, buf);
1134 //}
1135 }
1136 }
1137 private void addParagraphProperties(PAP pap, StringBuffer buf)
1138 {
1139 buf.append("<fo:block ");
1140 buf.append("text-align=\"" + getTextAlignment(pap._jc) + "\"\r\n");
1141 buf.append("linefeed-treatment=\"preserve\" ");
1142 buf.append("white-space-collapse=\"false\" ");
1143
1144 if(pap._fKeep > 0)
1145 {
1146 buf.append("keep-together.within-page=\"always\"\r\n");
1147 }
1148 if(pap._fKeepFollow > 0)
1149 {
1150 buf.append("keep-with-next.within-page=\"always\"\r\n");
1151 }
1152 if(pap._fPageBreakBefore > 0)
1153 {
1154 buf.append("break-before=\"page\"\r\n");
1155 }
1156 if(pap._fNoAutoHyph == 0)
1157 {
1158 buf.append("hyphenate=\"true\"\r\n");
1159 }
1160 else
1161 {
1162 buf.append("hyphenate=\"false\"\r\n");
1163 }
1164 if(pap._dxaLeft > 0)
1165 {
1166 buf.append("start-indent=\"" + ((float)pap._dxaLeft)/1440.0f + "in\"\r\n");
1167 }
1168 if(pap._dxaRight > 0)
1169 {
1170 buf.append("end-indent=\"" + ((float)pap._dxaRight)/1440.0f + "in\"\r\n");
1171 }
1172 if(pap._dxaLeft1 != 0)
1173 {
1174 buf.append("text-indent=\"" + ((float)pap._dxaLeft1)/1440.0f + "in\"\r\n");
1175 }
1176 if(pap._lspd[1] == 0)
1177 {
1178 //buf.append("line-height=\"" + ((float)pap._lspd[0])/1440.0f + "in\"\r\n");
1179 }
1180 addBorder(buf, pap._brcTop, "top");
1181 addBorder(buf, pap._brcBottom, "bottom");
1182 addBorder(buf, pap._brcLeft, "left");
1183 addBorder(buf, pap._brcRight, "right");
1184
1185 buf.append(">");
1186
1187 }
1188
1189 private void addCharacterProperties(CHP chp, StringBuffer buf)
1190 {
1191 buf.append("<fo:inline ");
1192 buf.append("font-family=\"" + _fonts.getFont(chp._ftcAscii) + "\" ");
1193 buf.append("font-size=\"" + (chp._hps / 2) + "pt\" ");
1194 buf.append("color=\"" + getColor(chp._ico) + "\" ");
1195 //not supported by fop
1196 //buf.append("letter-spacing=\"" + ((double)chp._dxaSpace)/1440.0f + "in\" ");
1197
1198 addBorder(buf, chp._brc, "top");
1199 addBorder(buf, chp._brc, "bottom");
1200 addBorder(buf, chp._brc, "left");
1201 addBorder(buf, chp._brc, "right");
1202
1203 if(chp._italic)
1204 {
1205 buf.append("font-style=\"italic\" ");
1206 }
1207 if(chp._bold)
1208 {
1209 buf.append("font-weight=\"bold\" ");
1210 }
1211 if(chp._fSmallCaps)
1212 {
1213 buf.append("font-variant=\"small-caps\" ");
1214 }
1215 if(chp._fCaps)
1216 {
1217 buf.append("text-transform=\"uppercase\" ");
1218 }
1219 if(chp._fStrike || chp._fDStrike)
1220 {
1221 buf.append("text-decoration=\"line-through\" ");
1222 }
1223 if(chp._fShadow)
1224 {
1225 int size = chp._hps/24;
1226 buf.append("text-shadow=\"" + size + "pt\"");
1227 }
1228 if(chp._fLowerCase)
1229 {
1230 buf.append("text-transform=\"lowercase\" ");
1231 }
1232 if(chp._kul > 0)
1233 {
1234 buf.append("text-decoration=\"underline\" ");
1235 }
1236 if(chp._highlighted)
1237 {
1238 buf.append("background-color=\"" + getColor(chp._icoHighlight) + "\" ");
1239 }
1240 if(chp._paddingStart != 0)
1241 {
1242 buf.append("padding-start=\"" + (float)chp._paddingStart/1440.0f + "in\" ");
1243 }
1244 if(chp._paddingEnd != 0)
1245 {
1246 buf.append("padding-end=\"" + (float)chp._paddingEnd/1440.0f + "in\" ");
1247 }
1248 buf.append(">");
1249 }
1250 private void addStaticContent(String flowName, HeaderFooter content)
1251 {
1252 _bodyBuffer.append("<fo:static-content flow-name=\"" + flowName + "\">");
1253 //_bodyBuffer.append("<fo:float float=\"before\">");
1254 addBlockContent(content.getStart(), content.getEnd(),_text, _paragraphTable, _characterTable);
1255 //_bodyBuffer.append("</fo:float>");
1256 _bodyBuffer.append("</fo:static-content>");
1257
1258 }
1259 private String getBulletText(LVL lvl, PAP pap)
1260 {
1261 StringBuffer bulletBuffer = new StringBuffer();
1262 for(int x = 0; x < lvl._xst.length; x++)
1263 {
1264 if(lvl._xst[x] < 9)
1265 {
1266 LVL numLevel = _listTables.getLevel(pap._ilfo, lvl._xst[x]);
1267 int num = numLevel._iStartAt;
1268 if(lvl == numLevel)
1269 {
1270 numLevel._iStartAt++;
1271 }
1272 else if(num > 1)
1273 {
1274 num--;
1275 }
1276 bulletBuffer.append(NumberFormatter.getNumber(num, lvl._nfc));
1277
1278 }
1279 else
1280 {
1281 bulletBuffer.append(lvl._xst[x]);
1282 }
1283
1284 }
1285 return bulletBuffer.toString();
1286 }
1287 /**
1288 * finds all chpx's that are between start and end
1289 */
1290 private ArrayList findProperties(int start, int end, BTreeSet.BTreeNode root)
1291 {
1292 ArrayList results = new ArrayList();
1293 BTreeSet.Entry[] entries = root.entries;
1294
1295 for(int x = 0; x < entries.length; x++)
1296 {
1297 if(entries[x] != null)
1298 {
1299 BTreeSet.BTreeNode child = entries[x].child;
1300 PropertyNode xNode = (PropertyNode)entries[x].element;
1301 if(xNode != null)
1302 {
1303 int xStart = xNode.getStart();
1304 int xEnd = xNode.getEnd();
1305 if(xStart < end)
1306 {
1307 if(xStart >= start)
1308 {
1309 if(child != null)
1310 {
1311 ArrayList beforeItems = findProperties(start, end, child);
1312 results.addAll(beforeItems);
1313 }
1314 results.add(xNode);
1315 }
1316 else if(start < xEnd)
1317 {
1318 results.add(xNode);
1319 //break;
1320 }
1321 }
1322 else
1323 {
1324 if(child != null)
1325 {
1326 ArrayList beforeItems = findProperties(start, end, child);
1327 results.addAll(beforeItems);
1328 }
1329 break;
1330 }
1331 }
1332 else if(child != null)
1333 {
1334 ArrayList afterItems = findProperties(start, end, child);
1335 results.addAll(afterItems);
1336 }
1337 }
1338 else
1339 {
1340 break;
1341 }
1342 }
1343 return results;
1344 }
1345 private void openPage(String page, String type)
1346 {
1347 _bodyBuffer.append("<fo:page-sequence master-reference=\"" + page + "\">\r\n");
1348 }
1349 private void openFlow()
1350 {
1351 _bodyBuffer.append("<fo:flow flow-name=\"xsl-region-body\">\r\n");
1352 }
1353 private void closeFlow()
1354 {
1355 _bodyBuffer.append("</fo:flow>\r\n");
1356 }
1357 private void closePage()
1358 {
1359 _bodyBuffer.append("</fo:page-sequence>\r\n");
1360 }
1361 private void closeLine(StringBuffer buf)
1362 {
1363 buf.append("</fo:inline>");
1364 }
1365 private void closeBlock(StringBuffer buf)
1366 {
1367 buf.append("</fo:block>\r\n");
1368 }
1369 private ArrayList findPAPProperties(int start, int end, BTreeSet.BTreeNode root)
1370 {
1371 ArrayList results = new ArrayList();
1372 BTreeSet.Entry[] entries = root.entries;
1373
1374 for(int x = 0; x < entries.length; x++)
1375 {
1376 if(entries[x] != null)
1377 {
1378 BTreeSet.BTreeNode child = entries[x].child;
1379 PapxNode papxNode = (PapxNode)entries[x].element;
1380 if(papxNode != null)
1381 {
1382 int papxStart = papxNode.getStart();
1383 if(papxStart < end)
1384 {
1385 if(papxStart >= start)
1386 {
1387 if(child != null)
1388 {
1389 ArrayList beforeItems = findPAPProperties(start, end, child);
1390 results.addAll(beforeItems);
1391 }
1392 results.add(papxNode);
1393 }
1394 }
1395 else
1396 {
1397 if(child != null)
1398 {
1399 ArrayList beforeItems = findPAPProperties(start, end, child);
1400 results.addAll(beforeItems);
1401 }
1402 break;
1403 }
1404 }
1405 else if(child != null)
1406 {
1407 ArrayList afterItems = findPAPProperties(start, end, child);
1408 results.addAll(afterItems);
1409 }
1410 }
1411 else
1412 {
1413 break;
1414 }
1415 }
1416 return results;
1417 }
1418
1419 private String createPageMaster(SEP sep, String type, int section,
1420 String regionBefore, String regionAfter)
1421 {
1422 float height = ((float)sep._yaPage)/1440.0f;
1423 float width = ((float)sep._xaPage)/1440.0f;
1424 float leftMargin = ((float)sep._dxaLeft)/1440.0f;
1425 float rightMargin = ((float)sep._dxaRight)/1440.0f;
1426 float topMargin = ((float)sep._dyaTop)/1440.0f;
1427 float bottomMargin = ((float)sep._dyaBottom)/1440.0f;
1428
1429 //add these to the header
1430 String thisPage = type + "-page" + section;
1431
1432 _headerBuffer.append("<fo:simple-page-master master-name=\"" +
1433 thisPage + "\"\r\n");
1434 _headerBuffer.append("page-height=\"" + height + "in\"\r\n");
1435 _headerBuffer.append("page-width=\"" + width + "in\"\r\n");
1436 _headerBuffer.append(">\r\n");
1437
1438
1439
1440 _headerBuffer.append("<fo:region-body ");
1441 //top right bottom left
1442
1443 _headerBuffer.append("margin=\"" + topMargin + "in " + rightMargin + "in " +
1444 bottomMargin + "in " + leftMargin + "in\"\r\n");
1445
1446 //String style = null;
1447 //String color = null;
1448 addBorder(_headerBuffer, sep._brcTop, "top");
1449 addBorder(_headerBuffer, sep._brcBottom, "bottom");
1450 addBorder(_headerBuffer, sep._brcLeft, "left");
1451 addBorder(_headerBuffer, sep._brcRight, "right");
1452
1453 if(sep._ccolM1 > 0)
1454 {
1455 _headerBuffer.append("column-count=\"" + (sep._ccolM1 + 1) + "\" ");
1456 if(sep._fEvenlySpaced)
1457 {
1458 _headerBuffer.append("column-gap=\"" + ((float)(sep._dxaColumns))/1440.0f + "in\"");
1459 }
1460 else
1461 {
1462 _headerBuffer.append("column-gap=\"0.25in\"");
1463 }
1464 }
1465 _headerBuffer.append("/>\r\n");
1466
1467 if(regionBefore != null)
1468 {
1469 _headerBuffer.append(regionBefore);
1470 }
1471 if(regionAfter != null)
1472 {
1473 _headerBuffer.append(regionAfter);
1474 }
1475
1476 _headerBuffer.append("</fo:simple-page-master>\r\n");
1477 return thisPage;
1478 }
1479 private void addBorder(StringBuffer buf, short[] brc, String where)
1480 {
1481 if((brc[0] & 0xff00) != 0 && brc[0] != -1)
1482 {
1483 int type = (brc[0] & 0xff00) >> 8;
1484 float width = ((float)(brc[0] & 0x00ff))/8.0f;
1485 String style = getBorderStyle(brc[0]);
1486 String color = getColor(brc[1] & 0x00ff);
1487 String thickness = getBorderThickness(brc[0]);
1488 buf.append("border-" + where + "-style=\"" + style + "\"\r\n");
1489 buf.append("border-" + where + "-color=\"" + color + "\"\r\n");
1490 buf.append("border-" + where + "-width=\"" + width + "pt\"\r\n");
1491 }
1492 }
1493 public void closeDoc()
1494 {
1495 _headerBuffer.append("</fo:layout-master-set>");
1496 _bodyBuffer.append("</fo:root>");
1497 //_headerBuffer.append();
1498
1499 //test code
1500 try
1501 {
1502 OutputStreamWriter test = new OutputStreamWriter(new FileOutputStream(_outName), "8859_1");
1503 test.write(_headerBuffer.toString());
1504 test.write(_bodyBuffer.toString());
1505 test.flush();
1506 test.close();
1507 }
1508 catch(Throwable t)
1509 {
1510 t.printStackTrace();
1511 }
1512 }
1513 private String getBorderThickness(int style)
1514 {
1515 switch(style)
1516 {
1517 case 1:
1518 return "medium";
1519 case 2:
1520 return "thick";
1521 case 3:
1522 return "medium";
1523 case 5:
1524 return "thin";
1525 default:
1526 return "medium";
1527 }
1528 }
1529
1530
1531 private String getColor(int ico)
1532 {
1533 switch(ico)
1534 {
1535 case 1:
1536 return "black";
1537 case 2:
1538 return "blue";
1539 case 3:
1540 return "cyan";
1541 case 4:
1542 return "green";
1543 case 5:
1544 return "magenta";
1545 case 6:
1546 return "red";
1547 case 7:
1548 return "yellow";
1549 case 8:
1550 return "white";
1551 case 9:
1552 return "darkblue";
1553 case 10:
1554 return "darkcyan";
1555 case 11:
1556 return "darkgreen";
1557 case 12:
1558 return "darkmagenta";
1559 case 13:
1560 return "darkred";
1561 case 14:
1562 return "darkyellow";
1563 case 15:
1564 return "darkgray";
1565 case 16:
1566 return "lightgray";
1567 default:
1568 return "black";
1569 }
1570 }
1571
1572 private String getBorderStyle(int type)
1573 {
1574
1575 switch(type)
1576 {
1577 case 1:
1578 case 2:
1579 return "solid";
1580 case 3:
1581 return "double";
1582 case 5:
1583 return "solid";
1584 case 6:
1585 return "dotted";
1586 case 7:
1587 case 8:
1588 return "dashed";
1589 case 9:
1590 return "dotted";
1591 case 10:
1592 case 11:
1593 case 12:
1594 case 13:
1595 case 14:
1596 case 15:
1597 case 16:
1598 case 17:
1599 case 18:
1600 case 19:
1601 return "double";
1602 case 20:
1603 return "solid";
1604 case 21:
1605 return "double";
1606 case 22:
1607 return "dashed";
1608 case 23:
1609 return "dashed";
1610 case 24:
1611 return "ridge";
1612 case 25:
1613 return "grooved";
1614 default:
1615 return "solid";
1616 }
1617 }
1618 /**
1619 * creates the List data
1620 *
1621 * @param tableStream Main table stream buffer.
1622 */
1623 private void createListTables(byte[] tableStream)
1624 {
1625
1626
1627 int lfoOffset = LittleEndian.getInt(_header, 0x2ea);
1628 int lfoSize = LittleEndian.getInt(_header, 0x2ee);
1629 byte[] plflfo = new byte[lfoSize];
1630
1631 System.arraycopy(tableStream, lfoOffset, plflfo, 0, lfoSize);
1632
1633 int lstOffset = LittleEndian.getInt(_header, 0x2e2);
1634 int lstSize = LittleEndian.getInt(_header, 0x2e2);
1635 if(lstOffset > 0 && lstSize > 0)
1636 {
1637 lstSize = lfoOffset - lstOffset;
1638 byte[] plcflst = new byte[lstSize];
1639 System.arraycopy(tableStream, lstOffset, plcflst, 0, lstSize);
1640 _listTables = new ListTables(plcflst, plflfo);
1641 }
1642
1643 }
1644 /**
1645 * Creates the documents StyleSheet
1646 *
1647 * @param tableStream Main table stream buffer.
1648 *
1649 */
1650 private void createStyleSheet(byte[] tableStream)
1651 {
1652 int stshIndex = LittleEndian.getInt(_header, 0xa2);
1653 int stshSize = LittleEndian.getInt(_header, 0xa6);
1654 byte[] stsh = new byte[stshSize];
1655 System.arraycopy(tableStream, stshIndex, stsh, 0, stshSize);
1656
1657 _styleSheet = new StyleSheet(stsh);
1658
1659 }
1660 /**
1661 * creates the Font table
1662 *
1663 * @param tableStream Main table stream buffer.
1664 */
1665 private void createFontTable(byte[] tableStream)
1666 {
1667 int fontTableIndex = LittleEndian.getInt(_header, 0x112);
1668 int fontTableSize = LittleEndian.getInt(_header, 0x116);
1669 byte[] fontTable = new byte[fontTableSize];
1670 System.arraycopy(tableStream, fontTableIndex, fontTable, 0, fontTableSize);
1671 _fonts = new FontTable(fontTable);
1672 }
1673
1674
1675 private void overrideCellBorder(int row, int col, int height,
1676 int width, TC tc, TAP tap)
1677 {
1678
1679 if(row == 0)
1680 {
1681 if(tc._brcTop[0] == 0 || tc._brcTop[0] == -1)
1682 {
1683 tc._brcTop = tap._brcTop;
1684 }
1685 if(tc._brcBottom[0] == 0 || tc._brcBottom[0] == -1)
1686 {
1687 tc._brcBottom = tap._brcHorizontal;
1688 }
1689 }
1690 else if(row == (height - 1))
1691 {
1692 if(tc._brcTop[0] == 0 || tc._brcTop[0] == -1)
1693 {
1694 tc._brcTop = tap._brcHorizontal;
1695 }
1696 if(tc._brcBottom[0] == 0 || tc._brcBottom[0] == -1)
1697 {
1698 tc._brcBottom = tap._brcBottom;
1699 }
1700 }
1701 else
1702 {
1703 if(tc._brcTop[0] == 0 || tc._brcTop[0] == -1)
1704 {
1705 tc._brcTop = tap._brcHorizontal;
1706 }
1707 if(tc._brcBottom[0] == 0 || tc._brcBottom[0] == -1)
1708 {
1709 tc._brcBottom = tap._brcHorizontal;
1710 }
1711 }
1712 if(col == 0)
1713 {
1714 if(tc._brcLeft[0] == 0 || tc._brcLeft[0] == -1)
1715 {
1716 tc._brcLeft = tap._brcLeft;
1717 }
1718 if(tc._brcRight[0] == 0 || tc._brcRight[0] == -1)
1719 {
1720 tc._brcRight = tap._brcVertical;
1721 }
1722 }
1723 else if(col == (width - 1))
1724 {
1725 if(tc._brcLeft[0] == 0 || tc._brcLeft[0] == -1)
1726 {
1727 tc._brcLeft = tap._brcVertical;
1728 }
1729 if(tc._brcRight[0] == 0 || tc._brcRight[0] == -1)
1730 {
1731 tc._brcRight = tap._brcRight;
1732 }
1733 }
1734 else
1735 {
1736 if(tc._brcLeft[0] == 0 || tc._brcLeft[0] == -1)
1737 {
1738 tc._brcLeft = tap._brcVertical;
1739 }
1740 if(tc._brcRight[0] == 0 || tc._brcRight[0] == -1)
1741 {
1742 tc._brcRight = tap._brcVertical;
1743 }
1744 }
1745 }
1746 private void printTable()
1747 {
1748 if(_table != null)
1749 {
1750 int size = _table.size();
1751
1752 //local buffers for the table
1753 StringBuffer tableHeaderBuffer = new StringBuffer();
1754 StringBuffer tableBodyBuffer = new StringBuffer();
1755
1756 for(int x = 0; x < size; x++)
1757 {
1758 StringBuffer rowBuffer = tableBodyBuffer;
1759 TableRow row = (TableRow)_table.get(x);
1760 TAP tap = row.getTAP();
1761 ArrayList cells = row.getCells();
1762
1763 if(tap._fTableHeader)
1764 {
1765 rowBuffer = tableHeaderBuffer;
1766 }
1767 rowBuffer.append("<fo:table-row ");
1768 if(tap._dyaRowHeight > 0)
1769 {
1770 rowBuffer.append("height=\"" + ((float)tap._dyaRowHeight)/1440.0f + "in\" ");
1771 }
1772 if(tap._fCantSplit)
1773 {
1774 rowBuffer.append("keep-together=\"always\" ");
1775 }
1776 rowBuffer.append(">");
1777 //add cells
1778 for(int y = 0; y < tap._itcMac; y++)
1779 {
1780 TC tc = tap._rgtc[y];
1781 overrideCellBorder(x, y, size, tap._itcMac, tc, tap);
1782 rowBuffer.append("<fo:table-cell ");
1783 rowBuffer.append("width=\"" + ((float)(tap._rgdxaCenter[y+1] - tap._rgdxaCenter[y]))/1440.0f + "in\" ");
1784 rowBuffer.append("padding-start=\"" + ((float)tap._dxaGapHalf)/1440.0f + "in\" ");
1785 rowBuffer.append("padding-end=\"" + ((float)tap._dxaGapHalf)/1440.0f + "in\" ");
1786 addBorder(rowBuffer, tc._brcTop, "top");
1787 addBorder(rowBuffer, tc._brcLeft, "left");
1788 addBorder(rowBuffer, tc._brcBottom, "bottom");
1789 addBorder(rowBuffer, tc._brcRight, "right");
1790 rowBuffer.append(">");
1791 rowBuffer.append((String)cells.get(y));
1792 rowBuffer.append("</fo:table-cell>");
1793 }
1794 rowBuffer.append("</fo:table-row>");
1795 }
1796 StringBuffer tableBuffer = new StringBuffer();
1797 tableBuffer.append("<fo:table>");
1798 if(tableHeaderBuffer.length() > 0)
1799 {
1800 tableBuffer.append("<fo:table-header>");
1801 tableBuffer.append(tableHeaderBuffer.toString());
1802 tableBuffer.append("</fo:table-header>");
1803 }
1804 tableBuffer.append("<fo:table-body>");
1805 tableBuffer.append(tableBodyBuffer.toString());
1806 tableBuffer.append("</fo:table-body>");
1807 tableBuffer.append("</fo:table>");
1808 _bodyBuffer.append(tableBuffer.toString());
1809 _table = null;
1810 }
1811 }
1812 private void initPclfHdd(byte[] tableStream)
1813 {
1814 int size = Utils.convertBytesToInt(_header, 0xf6);
1815 int pos = Utils.convertBytesToInt(_header, 0xf2);
1816
1817 _plcfHdd = new byte[size];
1818
1819 System.arraycopy(tableStream, pos, _plcfHdd, 0, size);
1820 }
1821
1822
1823
1824
1825 }