libmswrite.cpp
1/* This file is part of the LibMSWrite Library
2 Copyright (C) 2001-2003 Clarence Dang <clarencedang@users.sourceforge.net>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License Version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License Version 2 for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 Version 2 along with this library; see the file COPYING.LIB. If not,
15 write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301, USA.
17
18 LibMSWrite Project Website:
19 http://sourceforge.net/projects/libmswrite/
20*/
21
22#include <assert.h>
23#include <stdio.h>
24#include <string.h>
25#include <ctype.h>
26
27#include "libmswrite.h"
28
29namespace MSWrite
30{
31 FormatInfo::FormatInfo () : m_nextChar (0),
32 m_leftMargin (0xFFFF), m_rightMargin (0xFFFF),
33 m_fontTable (NULL)
34 {
35 // m_type =
36 }
37
38 FormatInfo::~FormatInfo ()
39 {
40 }
41
42 bool FormatInfo::readFromDevice (void)
43 {
44 CHECK_DEVICE;
45
46 #ifdef DEBUG_FORMATINFO
47 if (m_type == ParaType)
48 m_device->debug ("\n<<<< libmswrite.cpp::FormatInfo::readFromDevice (ParaType) >>>>\n");
49 else // if (m_type == CharType)
50 m_device->debug ("\n<<<< libmswrite.cpp::FormatInfo::readFromDevice (CharType) >>>>\n");
51 #endif
52
53 int formatInfoPageNum;
54 if (m_type == ParaType)
55 formatInfoPageNum = m_header->getNumPageParaInfo ();
56 else // if (m_type == CharType)
57 formatInfoPageNum = m_header->getNumPageCharInfo ();
58
59
60 #ifdef DEBUG_FORMATINFO
61 m_device->debug ("numpages format info=", formatInfoPageNum);
62 #endif
63
64 // you have to have information pages that cover the _entire_ document (if it's not empty that is)
65 if (m_header->getNumCharBytes () && formatInfoPageNum == 0)
66 {
67 if (m_type == ParaType)
68 {
69 ErrorAndQuit (Error::InvalidFormat, "no paragraph formatting information page\n");
70 }
71 else // if (m_type == CharType)
72 {
73 ErrorAndQuit (Error::InvalidFormat, "no character formatting information page\n");
74 }
75 }
76
77 // seek to start of info pages
78 if (!m_device->seek (((m_type == ParaType) ?
79 m_header->getPageParaInfo () : m_header->getPageCharInfo ()) * 128, SEEK_SET))
80 return false;
81
82 //
83 // read in every info page
84 // (this eats up all your memory...)
85 //
86 // The reason this is done is because infoPages (formatting information) are required
87 // in between reads of text and on most devices, continually seeking back and forth
88 // between the text pages and information pages is inconvenient and inefficient to say
89 // the least.
90 //
91 // Generally speaking there is more text than formatting information some it is probably
92 // cheaper (memory-wise) to cache the formatting in memory, rather than the text.
93 //
94 // A few more good reasons for caching formatting information:
95 //
96 // * we don't actually expand the formatting pages into the full FormatPointers
97 // and FormatProperty's (until begin()/next() are called) so we save quite a bit of
98 // memory
99 // * some filters need to know information like the number of objects in advance
100 // so an extra parse of the formatting information (already cached :)) is required
101 //
102 for (int i = 0; i < formatInfoPageNum; i++)
103 {
104 if (!m_formatInfoPageList.addToBack ())
105 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for an information page\n");
106
107 FormatInfoPage &fip = *m_formatInfoPageList.begin (false);
108 if (m_device->bad ()) return false;
109
110 fip.setHeader (m_header);
111 fip.setDevice (m_device);
112 fip.setType (m_type);
113
114 if (m_type == ParaType)
115 fip.setMargins (m_leftMargin, m_rightMargin);
116 else // if (m_type == CharType)
117 fip.setFontTable (m_fontTable);
118
119 if (!fip.readFromDevice ())
120 return false;
121 }
122
123 return true;
124 }
125
126 bool FormatInfo::writeToDevice (const void *defaultProperty)
127 {
128 CHECK_DEVICE;
129
130 #ifdef DEBUG_FORMATINFO
131 if (m_type == ParaType)
132 m_device->debug ("\n<<<< libmswrite.cpp::FormatInfo::writeToDevice (ParaType) >>>>\n");
133 else // if (m_type == CharType)
134 m_device->debug ("\n<<<< libmswrite.cpp::FormatInfo::writeToDevice (CharType) >>>>\n");
135 #endif
136
137 // set page number of formatting info in header
138 if (m_type == ParaType)
139 m_header->setPageParaInfo (m_device->tellInternal () / 128);
140 else // if (m_type == CharType)
141 {
142 // ms programmers were so space conservative that they didn't
143 // store charInfo page
144 //m_header->setPageCharInfo (m_device->tellInternal () / 128);
145 }
146
147 //
148 // No formatting table?
149 //
150 // Mimick what Write does and make one.
151 // It seems that only a fontTable is mandatory but this is conveniently
152 // updated when adding to a character formatting page.
153 //
154 if (!m_formatInfoPageList.getNumElements ())
155 {
156 #ifdef DEBUG_FORMATINFO
157 m_device->debug ("\tno formatting information pages, creating one\n");
158 #endif
159
160 // --- but we should only get in here if numCharBytes = 0 ---
161
162 if (m_header->getNumCharBytes ())
163 {
164 if (m_type == ParaType)
165 m_device->error (Error::Warn, "data but no paragraph formatting info\n");
166 else // if (m_type == CharType)
167 m_device->error (Error::Warn, "data but no character formatting info\n");
168 }
169
170 long currentOffset = m_device->tellInternal ();
171 if (!m_device->seekInternal (128 + m_header->getNumCharBytes (), SEEK_SET)) return false;
172 if (!add (defaultProperty, true/* force */)) return false;
173 if (!m_device->seekInternal (currentOffset, SEEK_SET)) return false;
174 }
175
176 List <FormatInfoPage>::Iterator it;
177 for (it = m_formatInfoPageList.begin (); it != m_formatInfoPageList.end (); ++it)
178 {
179 FormatInfoPage &fip = *it;
180
181 // OPT: should already have done in add but we have to handle the impexp case
182 fip.setHeader (m_header);
183 fip.setDevice (m_device);
184 fip.setType (m_type);
185
186 if (m_type == ParaType)
187 fip.setMargins (m_leftMargin, m_rightMargin);
188 else // if (m_type == CharType)
189 fip.setFontTable (m_fontTable);
190
191 if (!fip.writeToDevice ())
192 return false;
193 }
194
195 return true;
196 }
197
198 void *FormatInfo::begin (void)
199 {
200 m_nextChar = 0;
201
202 m_formatInfoPageIterator = m_formatInfoPageList.begin ();
203
204 if (m_formatInfoPageIterator == m_formatInfoPageList.end ())
205 return NULL;
206
207 void *ret = (*m_formatInfoPageIterator).begin ();
208
209 if (ret)
210 {
211 if (m_type == ParaType)
212 m_nextChar = ((FormatParaProperty *) ret)->getAfterEndCharByte ();
213 else // if (m_type == CharType)
214 m_nextChar = ((FormatCharProperty *) ret)->getAfterEndCharByte ();
215 }
216
217 return ret;
218 }
219
220 void *FormatInfo::next (void)
221 {
222 #ifdef CHECK_INTERNAL
223 if (!m_device)
224 {
225 CHECK_DEVICE_ERROR;
226 return NULL;
227 }
228 #endif
229
230 void *ret = NULL;
231 if ((*m_formatInfoPageIterator).end ())
232 {
233 m_formatInfoPageIterator++;
234
235 // out of pages?
236 if (m_formatInfoPageIterator == m_formatInfoPageList.end ())
237 return NULL;
238
239 if ((*m_formatInfoPageIterator).getFirstCharByte () != m_nextChar)
240 m_device->error (Error::Warn, "FormatInfoPage::firstCharByte does not flow on from nextChar\n");
241
242 ret = (*m_formatInfoPageIterator).begin ();
243 }
244
245 if (!ret)
246 ret = (*m_formatInfoPageIterator).next ();
247
248 if (ret)
249 {
250 if (m_type == ParaType)
251 m_nextChar = ((FormatParaProperty *) ret)->getAfterEndCharByte ();
252 else // if (m_type == CharType)
253 m_nextChar = ((FormatCharProperty *) ret)->getAfterEndCharByte ();
254 }
255
256 return ret;
257 }
258
259 bool FormatInfo::end (void) /*const*/
260 {
261 return m_formatInfoPageIterator == m_formatInfoPageList.end ();
262 }
263
264 bool FormatInfo::add (const void *property, const bool force)
265 {
266 CHECK_DEVICE;
267
268 #ifdef DEBUG_FORMATINFO
269 m_device->debug (">>>> FormatInfo::add <<<<\n");
270 #endif
271
272 DWord currentChar = m_device->tellInternal () - 128;
273
274 // so that export filter writers can be lazy...
275 if (m_nextChar == currentChar && !force)
276 {
277 #ifdef DEBUG_FORMATINFO
278 m_device->debug ("\tEmpty FormatProperty, ignoring\n");
279 #endif
280 return true;
281 }
282
283 bool needToAllocate = false;
284
285 if (m_formatInfoPageList.getNumElements ())
286 {
287 FormatInfoPage &fip = *m_formatInfoPageList.begin (false);
288 if (!fip.add (property))
289 {
290 // a real error
291 if (m_device->bad ())
292 return false;
293
294 needToAllocate = true;
295 }
296 }
297 else
298 needToAllocate = true;
299
300 if (needToAllocate)
301 {
302 #ifdef DEBUG_FORMATINFO
303 m_device->debug ("\tneedToAllocate=yes, FormatInfoPage::firstCharByte=", m_nextChar);
304 #endif
305
306 if (!m_formatInfoPageList.addToBack ())
307 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for new formatInfoPage\n");
308
309 FormatInfoPage &fip = *m_formatInfoPageList.begin (false);
310 fip.setDevice (m_device);
311 fip.setFirstCharByte (m_nextChar);
312 fip.setType (m_type);
313
314 if (m_type == ParaType)
315 fip.setMargins (m_leftMargin, m_rightMargin);
316 else // if (m_type == CharType)
317 {
318 assert (m_fontTable);
319 fip.setFontTable (m_fontTable);
320 }
321
322 if (!fip.add (property))
323 return false;
324 }
325
326 m_nextChar = currentChar;
327 return true;
328 }
329
330
331 Generator::Generator ()
332 {
333 }
334
335 Generator::~Generator ()
336 {
337 }
338
339 bool Generator::processText (const Byte *string, bool willReachEndOfParagraph)
340 {
341 //
342 // Look out for characters in the string and emit signals as appropriate:
343 //
344 // 1 pageNumber
345 // 10 newLine
346 // 13 carriageReturn
347 // 12 pageBreak
348 // 31 optionalHyphen
349 // ? text
350 //
351
352 const int outBufferMaxLen = 1024;
353 Byte outBuffer [outBufferMaxLen];
354 DWord outBufferLen = 0;
355
356 for (; *string; string++)
357 {
358 // buffer full?
359 if (outBufferLen >= outBufferMaxLen - 1)
360 {
361 // flush
362 outBuffer [outBufferMaxLen - 1] = '\0';
363 if (!writeText (outBuffer)) return false;
364 outBufferLen = 0;
365 }
366
367 switch (*string)
368 {
369 // write text, generate signals for special characters, write more text...
370 case 1: // pageNumber anchor
371 case 12: // pageBreak (some silly document might have this in the middle of a paragraph!)
372 case 13:
373 case 10: // newLine (some silly document _does_ have a newline in the middle of a paragraph!)
374 case 31: // optionalHyphen (aka "soft hyphen" an invisible hyphen, unless at end of line)
375 // output text before this character
376 if (outBufferLen)
377 {
378 outBuffer [outBufferLen] = 0; // null terminate
379 if (!writeText (outBuffer)) return false;
380 outBufferLen = 0;
381 }
382
383 // generate signal
384 switch (*string)
385 {
386 case 1: if (!writePageNumber ()) return false; break;
387 case 12: if (!writePageBreak ()) return false; break;
388 case 10: if (!writeNewLine (willReachEndOfParagraph && string [1] == 0)) return false; break;
389 case 13: if (!writeCarriageReturn ()) return false; break;
390 case 31: if (!writeOptionalHyphen ()) return false; break;
391 }
392
393 break;
394
395 // normal text character
396 default:
397 outBuffer [outBufferLen++] = *string;
398 break;
399 } // switch (*string) {
400 } // for (; *string; string++) {
401
402 // flush
403 if (outBufferLen)
404 {
405 outBuffer [outBufferLen] = 0;
406 if (!writeText (outBuffer)) return false;
407 }
408
409 return true;
410 }
411
412
413 InternalParser::InternalParser () : m_header (NULL),
414 m_sectionTable (NULL),
415 m_pageLayout (NULL),
416 m_pageTable (NULL),
417 m_fontTable (NULL),
418 m_paragraphInfo (NULL),
419 m_characterInfo (NULL),
420 m_image (NULL),
421 m_ole (NULL)
422 {
423 }
424
425 InternalParser::~InternalParser ()
426 {
427 delete m_ole;
428 delete m_image;
429
430 delete m_characterInfo;
431 delete m_paragraphInfo;
432 delete m_fontTable;
433 delete m_pageTable;
434 delete m_pageLayout;
435 delete m_sectionTable;
436 delete m_header;
437 }
438
439 // use this parser for "import" filters
440 bool InternalParser::parse (void)
441 {
442 if (!m_device)
443 {
444 fprintf (stderr, "INTERNAL ERROR: InternalParser::parse() called without a device\n");
445 return false; // cannot use ErrorAndQuit() because that calls m_device->error()
446 }
447
448 if (!m_generator)
449 ErrorAndQuit (Error::InternalError, "generator not passed to parser\n");
450
451 //
452 // allocate memory
453 //
454
455 m_header = new Header;
456 if (!m_header)
457 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for header\n");
458
459 m_sectionTable = new SectionTable;
460 if (!m_sectionTable)
461 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for sectionTable\n");
462
463 m_pageLayout = new PageLayout;
464 if (!m_pageLayout)
465 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for pageLayout\n");
466
467 m_pageTable = new PageTable;
468 if (!m_pageTable)
469 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for pageTable\n");
470
471 m_fontTable = new FontTable;
472 if (!m_fontTable)
473 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for fontTable\n");
474
475 m_paragraphInfo = new FormatInfo;
476 if (!m_paragraphInfo)
477 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for paragraphInfo\n");
478
479 m_characterInfo = new FormatInfo;
480 if (!m_characterInfo)
481 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for charInfo\n");
482
483 //
484 // Read everything from .WRI file
485 //
486
487 m_header->setDevice (m_device);
488 if (!m_header->readFromDevice ()) return false;
489 DWord numCharBytes = m_header->getNumCharBytes ();
490
491 m_sectionTable->setHeader (m_header);
492 m_sectionTable->setDevice (m_device);
493 if (!m_sectionTable->readFromDevice ())
494 return false;
495
496 m_pageLayout->setHeader (m_header);
497 m_pageLayout->setDevice (m_device);
498 if (!m_pageLayout->readFromDevice ())
499 return false;
500
501 m_pageTable->setPageNumberStart (m_pageLayout->getPageNumberStart ());
502 m_pageTable->setHeader (m_header);
503 m_pageTable->setDevice (m_device);
504 if (!m_pageTable->readFromDevice ())
505 return false;
506
507 m_fontTable->setHeader (m_header);
508 m_fontTable->setDevice (m_device);
509 if (!m_fontTable->readFromDevice ())
510 return false;
511
512 m_paragraphInfo->setHeader (m_header);
513 m_paragraphInfo->setDevice (m_device);
514 m_paragraphInfo->setType (ParaType);
515 m_paragraphInfo->setMargins (m_pageLayout->getLeftMargin (), m_pageLayout->getRightMargin ());
516 if (!m_paragraphInfo->readFromDevice ())
517 return false;
518
519 m_characterInfo->setHeader (m_header);
520 m_characterInfo->setDevice (m_device);
521 m_characterInfo->setType (CharType);
522 m_characterInfo->setFontTable (m_fontTable);
523 if (!m_characterInfo->readFromDevice ())
524 return false;
525
526#if 0
527 DocumentInfo documentInfo;
528 FormatParaProperty *paraProperty = (FormatParaProperty *) m_paragraphInfo->begin ();
529 int numObjects = 0;
530 while (paraProperty)
531 {
532 if (paraProperty->getIsObject ())
533 numObjects++;
534 if (paraProperty->getIsHeader ())
535 {
536 documentInfo.setHasHeader (true);
537 if (paraProperty->getIsOnFirstPage ())
538 documentInfo.setHasHeaderOnFirstPage (true);
539 }
540 if (paraProperty->getIsFooter ())
541 {
542 documentInfo.setHasFooter (true);
543 if (paraProperty->getIsOnFirstPage ())
544 documentInfo.setHasFooterOnFirstPage (true);
545 }
546 paraProperty = (FormatParaProperty *) m_paragraphInfo->next ();
547 }
548 documentInfo.setNumObjects (numObjects);
549#endif
550
551 // Get Ready!
552 //
553
554 enum Section { InNothing, InBody, InHeader, InFooter } inWhat = InNothing;
555
556 // must call writeBody*() even if document doesn't have one
557 bool wroteBody = false;
558
559 PagePointer *pp = m_pageTable->begin ();
560 if (m_device->bad ()) return false;
561
562 //
563 // Signal callbacks
564 //
565 #ifdef DEBUG_INTERNALPARSER
566 m_device->debug ("@@@ InternalParser: start of document write\n");
567 #endif
568
569 if (!m_generator->writeDocumentBegin (m_header->getMagic (), m_pageLayout)) return false;
570 m_generator->sigProgress (0);
571
572 // start of text
573 if (!m_device->seekInternal (1 * 128, SEEK_SET))
574 return false;
575
576 FormatParaProperty *paraProp = (FormatParaProperty *) m_paragraphInfo->begin ();
577 if (m_device->bad ()) return false;
578 FormatCharProperty *charProp = (FormatCharProperty *) m_characterInfo->begin ();
579 if (m_device->bad ()) return false;
580 DWord paraStartByte = 0;
581
582 if (numCharBytes) while (paraProp) // loop if not empty document
583 {
584 #ifdef DEBUG_INTERNALPARSER
585 m_device->debug ("@@@ InternalParser: Start of loop - section write\n");
586 #endif
587 //
588 // Section work
589 //
590
591 enum Section inWhatNext = InNothing;
592 if (paraProp->getIsFooter ())
593 inWhatNext = InFooter;
594 else if (paraProp->getIsHeader ())
595 inWhatNext = InHeader;
596 else
597 inWhatNext = InBody;
598
599 // beginning of a new section?
600 if (inWhatNext != inWhat)
601 {
602 // end last thing we were in
603 switch (inWhat)
604 {
605 case InFooter: if (!m_generator->writeFooterEnd ()) return false; else break;
606 case InHeader: if (!m_generator->writeHeaderEnd ()) return false; else break;
607 case InBody: if (!m_generator->writeBodyEnd ()) return false; else break;
608 default: break; // keep compiler happy
609 }
610
611 // start next section
612 switch (inWhat = inWhatNext)
613 {
614 case InFooter: if (!m_generator->writeFooterBegin ()) return false; else break;
615 case InHeader: if (!m_generator->writeHeaderBegin ()) return false; else break;
616 case InBody: if (!m_generator->writeBodyBegin ()) return false;
617 // if there's not pageTable, manually signal start of page for the first and last time
618 if (!pp)
619 if (!m_generator->writePageNew ())
620 return false;
621
622 wroteBody = true;
623 default: break; // keep compiler happy
624 }
625 }
626
627
628 //
629 // start paragraph
630 //
631
632 bool paraIsText = paraProp->getIsText ();
633 int objectType = ObjectType::NotObject;
634
635 DWord paraEndByte, paraAfterEndByte;
636 paraAfterEndByte = paraProp->getAfterEndCharByte ();
637 paraEndByte = paraAfterEndByte - 1;
638
639 if (paraIsText)
640 {
641 #ifdef DEBUG_INTERNALPARSER
642 m_device->debug ("@@@ InternalParser: Start of paragraph write\n");
643 #endif
644
645 // signal paragraph
646 if (!m_generator->writeParaInfoBegin (paraProp, NULL, NULL)) return false;
647 }
648 else
649 {
650 //
651 // Determine whether the object is OLE or not
652 // Yes, I know this isn't entirely clean but
653 // name a cleaner and more efficient way of doing this...
654 //
655
656 Byte data [2];
657 Word mappingMode;
658
659 if (!m_device->readInternal (data, 2)) return false;
660 ReadWord (mappingMode, data);
661 if (!m_device->seekInternal (-2, SEEK_CUR)) return false; // ungetc()x2
662
663 switch (mappingMode)
664 {
665 case 0xE4:
666 objectType = ObjectType::OLE;
667
668 m_ole = new OLE;
669 if (!m_ole)
670 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for OLE object\n");
671
672 m_ole->setDevice (m_device);
673 if (!m_ole->readFromDevice ())
674 return false;
675
676 if (!m_generator->writeParaInfoBegin (paraProp, m_ole, NULL)) return false;
677 break;
678 case 0xE3: // monochrome bitmap?
679 objectType = ObjectType::BMP;
680
681 m_image = new Image;
682 if (!m_image)
683 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for BMP image object\n");
684
685 m_image->setDevice (m_device);
686 if (!m_image->readFromDevice ())
687 return false;
688
689 if (!m_generator->writeParaInfoBegin (paraProp, NULL, m_image)) return false;
690 break;
691 default:
692 objectType = ObjectType::WMF;
693
694 m_image = new Image;
695 if (!m_image)
696 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for WMF image object\n");
697
698 m_image->setDevice (m_device);
699 if (!m_image->readFromDevice ())
700 return false;
701
702 if (!m_generator->writeParaInfoBegin (paraProp, NULL, m_image)) return false;
703 break;
704 }
705
706 // image: BMP/WMF
707 if (objectType != ObjectType::OLE)
708 {
709 if (m_image->getNumHeaderBytes () + m_image->getNumDataBytes ()
710 != paraAfterEndByte - paraStartByte)
711 {
712 m_device->error (Error::Warn, "imageHeader: numHeaderBytes + numDataBytes != paragraph length\n");
713
714 // we had better seek to where the paragraphs expect us to be to avoid trouble
715 if (!m_device->seekInternal (paraAfterEndByte + 128, SEEK_SET)) return false;
716 }
717 }
718 }
719
720 #ifdef DEBUG_INTERNALPARSER
721 m_device->debug ("@@@ InternalParser: Start of text write\n");
722 #endif
723
724 DWord uptoByte = paraStartByte;
725
726 // loop until we hit end of paragraph (getting one CharProperty on every iteration)
727 while (uptoByte < paraAfterEndByte)
728 {
729 if (charProp)
730 {
731 if (uptoByte >= charProp->getAfterEndCharByte ())
732 {
733 charProp = (FormatCharProperty *) m_characterInfo->next ();
734 if (m_device->bad ()) return false;
735 }
736
737 #ifdef DEBUG_INTERNALPARSER
738 m_device->debug ("@@@ InternalParser: character write\n");
739 #endif
740
741 if (!m_generator->writeCharInfoBegin (charProp)) return false;
742 }
743
744 // ultimately aim for end of CharProperty block; if that's not possible, aim for end of paragraph
745 DWord aimUltimateByte = charProp->getEndCharByte () > paraEndByte ? paraEndByte : charProp->getEndCharByte ();
746
747 while (uptoByte <= aimUltimateByte)
748 {
749 // flag to tell us to generate pageNewWrite/pageTable signal, after writing some normal text
750 bool pageTableAck = false;
751
752 // short-term goal (before end of CharProperty, ParaProperty or page)
753 DWord aimByte = aimUltimateByte;
754
755 // we want to efficiently send pageNewWrite signals which might be sandwhiched inside
756 // a CharProperty block (note: we do NOT end and then restart a CharProperty block because of a writePageNew())
757 if (pp)
758 {
759 if (pp->getFirstCharByte () <= aimByte)
760 {
761 pageTableAck = true;
762 if (pp->getFirstCharByte ())
763 // - 1 is because we want the PageNew signal called before the character
764 aimByte = pp->getFirstCharByte () - 1;
765 else
766 aimByte = 0xFFFFFFFF; // 0 - 1 :)
767
768 #ifdef DEBUG_INTERNALPARSER
769 m_device->debug ("@@@ InternalParser: writePageNew pending\n");
770 #endif
771 }
772 }
773
774 // write text (using buffering)
775 while (uptoByte <= aimByte && aimByte != 0xFFFFFFFF)
776 {
777 const DWord amountToRead = aimByte - uptoByte + 1 > 1023
778 ? 1023 : aimByte - uptoByte + 1;
779
780 if (paraIsText)
781 {
782 Byte buffer [1024];
783
784 if (!m_device->readInternal (buffer, amountToRead)) return false;
785
786 buffer [amountToRead] = '\0';
787 if (!m_generator->processText (buffer, uptoByte + amountToRead - 1 == paraEndByte)) return false;
788 }
789
790 uptoByte += amountToRead;
791 } // while (uptoByte <= aimByte && aimByte != 0xFFFFFFFF) {
792
793 // generate pageNewWrite/pageTable signal, if requested
794 if (pageTableAck)
795 {
796 #ifdef DEBUG_INTERNALPARSER
797 m_device->debug ("@@@ InternalParser: writePageNew\n");
798 #endif
799
800 if (!m_generator->writePageNew (pp->getPageNumber ())) return false;
801
802 pp = m_pageTable->next ();
803 if (m_device->bad ()) return false;
804 }
805 } // while (uptoByte <= aimUltimateByte) {
806
807
808 #ifdef DEBUG_INTERNALPARSER
809 m_device->debug ("@@@ InternalParser: character end write\n");
810 #endif
811
812 // end char info
813 if (charProp)
814 if (!m_generator->writeCharInfoEnd (charProp, uptoByte == paraAfterEndByte))
815 return false;
816
817 } // while (uptoByte < paraAfterEndByte) {
818
819
820 //
821 // ouptut object
822 //
823
824 if (!paraIsText)
825 {
826 if (objectType == ObjectType::OLE)
827 {
828 if (!m_generator->writeBinary (m_ole->getExternalObject (), m_ole->getExternalObjectSize ()))
829 return false;
830 }
831 else // if (objectType == ObjectType::BMP || objectType == ObjectType::WMF)
832 {
833 if (!m_generator->writeBinary (m_image->getExternalImage (), m_image->getExternalImageSize ()))
834 return false;
835 }
836 }
837
838 //
839 // end paragraph
840 //
841
842 if (paraIsText)
843 {
844 // end paragraph
845 if (!m_generator->writeParaInfoEnd (paraProp, NULL)) return false;
846 }
847 else
848 {
849 switch (objectType)
850 {
851 case ObjectType::OLE:
852 if (!m_generator->writeParaInfoEnd (paraProp, m_ole, NULL)) return false;
853 delete m_ole;
854 m_ole = NULL;
855 break;
856 case ObjectType::BMP:
857 if (!m_generator->writeParaInfoEnd (paraProp, NULL, m_image)) return false;
858 delete m_image;
859 m_image = NULL;
860 break;
861 case ObjectType::WMF:
862 if (!m_generator->writeParaInfoEnd (paraProp, NULL, m_image)) return false;
863 delete m_image;
864 m_image = NULL;
865 break;
866 }
867 }
868
869 paraStartByte = paraAfterEndByte;
870
871 // numCharBytes != 0 because we checked it before we entered the loop
872 m_generator->sigProgress (paraStartByte * 100 / numCharBytes);
873
874 // get next paragraph properties
875 paraProp = (FormatParaProperty *) m_paragraphInfo->next ();
876 if (m_device->bad ()) return false;
877 }
878
879 // end last thing we were in
880 switch (inWhat)
881 {
882 case InFooter: if (!m_generator->writeFooterEnd ()) return false; else break;
883 case InHeader: if (!m_generator->writeHeaderEnd ()) return false; else break;
884 case InBody: if (!m_generator->writeBodyEnd ()) return false; else break;
885 default: break; // keep compiler happy
886 }
887
888 // didn't output a body (usually due to a blank document)
889 if (!wroteBody)
890 {
891 #ifdef DEBUG_INTERNALPARSER
892 m_device->debug ("@@@ InternalParser: did not write body, writing one now\n");
893 #endif
894
895 if (!m_generator->writeBodyBegin ()) return false;
896 if (!m_generator->writeBodyEnd ()) return false;
897 }
898
899 m_generator->sigProgress (100);
900 if (!m_generator->writeDocumentEnd (m_header->getMagic (), m_pageLayout)) return false;
901
902 return true;
903 }
904
905
906 InternalGenerator::InternalGenerator () : m_header (NULL),
907 m_sectionTable (NULL),
908 m_pageLayout (NULL),
909 m_pageTable (NULL),
910 m_fontTable (NULL),
911 m_paragraphInfo (NULL),
912 m_characterInfo (NULL),
913 m_image (NULL),
914 m_ole (NULL)
915 {
916 }
917
918 InternalGenerator::~InternalGenerator ()
919 {
920 delete m_ole;
921 delete m_image;
922
923 delete m_characterInfo;
924 delete m_paragraphInfo;
925 delete m_fontTable;
926 delete m_pageTable;
927 delete m_pageLayout;
928 delete m_sectionTable;
929 delete m_header;
930 }
931
932 bool InternalGenerator::seekNextPage (void)
933 {
934 return m_device->seekInternal ((m_device->tellInternal () + 127) / 128 * 128, SEEK_SET);
935 }
936
937 bool InternalGenerator::writeDocumentBegin (const Word, const PageLayout *pageLayout)
938 {
939 if (!m_device)
940 {
941 fprintf (stderr, "INTERNAL ERROR: InternalGenerator::writeDocumentBegin() called without a device\n");
942 return false; // cannot use ErrorAndQuit() because that calls m_device->error()
943 }
944
945 m_header = new Header;
946 if (!m_header)
947 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for header\n");
948 m_header->setDevice (m_device);
949
950 m_pageLayout = new PageLayout;
951 if (!m_pageLayout)
952 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for pageLayout\n");
953 m_pageLayout->setDevice (m_device);
954
955 m_sectionTable = new SectionTable;
956 if (!m_sectionTable)
957 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for sectionTable\n");
958 m_sectionTable->setDevice (m_device);
959
960 m_pageTable = new PageTable;
961 if (!m_pageTable)
962 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for pageTable\n");
963 m_pageTable->setDevice (m_device);
964
965 m_fontTable = new FontTable;
966 if (!m_fontTable)
967 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for fontTable\n");
968 m_fontTable->setDevice (m_device);
969
970 m_paragraphInfo = new FormatInfo;
971 if (!m_paragraphInfo)
972 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for paragraphInfo\n");
973 m_paragraphInfo->setType (ParaType);
974 m_paragraphInfo->setDevice (m_device);
975
976 m_characterInfo = new FormatInfo;
977 if (!m_characterInfo)
978 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for characterInfo\n");
979 m_characterInfo->setType (CharType);
980 m_characterInfo->setDevice (m_device);
981
982 *m_pageLayout = *pageLayout;
983
984 // get ready to output text
985 if (!m_device->seekInternal (1 * 128, SEEK_SET)) return false;
986
987 return true;
988 }
989
990 bool InternalGenerator::writeDocumentEnd (const Word format, const PageLayout *)
991 {
992 #ifdef DEBUG_INTERNALGENERATOR
993 m_device->debug ("!!!! InternalGenerator: writeDocumentEnd\n");
994 #endif
995
996 m_header->setNumCharBytes (m_device->tellInternal () - 128);
997
998 #ifdef DEBUG_INTERNALGENERATOR
999 m_device->debug ("before charinfo seek next page: ", m_device->tellInternal ());
1000 #endif
1001 if (!seekNextPage ()) return false; // from text pages
1002 #ifdef DEBUG_INTERNALGENERATOR
1003 m_device->debug ("!!!! InternalGenerator: Write characterInfo; page=", m_device->tellInternal () / 128);
1004 #endif
1005 FormatCharProperty defaultCharProperty;
1006 defaultCharProperty.setFontTable (m_fontTable);
1007 Font defaultFont ((const Byte *) "Arial"); // better than "unknown", I guess
1008 defaultCharProperty.setFont (&defaultFont);
1009
1010 // write characterInfo pages
1011 m_characterInfo->setDevice (m_device);
1012 m_characterInfo->setHeader (m_header);
1013 m_characterInfo->setFontTable (m_fontTable);
1014 if (!m_characterInfo->writeToDevice (&defaultCharProperty)) return false;
1015
1016 #ifdef DEBUG_INTERNALGENERATOR
1017 m_device->debug ("before parainfo seek next page: ", m_device->tellInternal ());
1018 #endif
1019 if (!seekNextPage ()) return false;
1020 #ifdef DEBUG_INTERNALGENERATOR
1021 m_device->debug ("!!!! InternalGenerator: Write paragraphInfo; page=", m_device->tellInternal () / 128);
1022 #endif
1023 FormatParaProperty defaultParaProperty;
1024 defaultParaProperty.setMargins (m_pageLayout->getLeftMargin (), m_pageLayout->getRightMargin ());
1025
1026 // write paragraphInfo pages
1027 m_paragraphInfo->setDevice (m_device);
1028 m_paragraphInfo->setHeader (m_header);
1029 m_paragraphInfo->setMargins (m_pageLayout->getLeftMargin (), m_pageLayout->getRightMargin ());
1030 if (!m_paragraphInfo->writeToDevice (&defaultParaProperty)) return false;
1031
1032 #ifdef DEBUG_INTERNALGENERATOR
1033 m_device->debug ("!!!! InternalGenerator: Write footnoteTable\n");
1034 #endif
1035
1036 // there's "no such thing (tm)" as a FootnoteTable
1037 m_header->setPageFootnoteTable ((m_device->tellInternal () + 127) / 128);
1038
1039 if (!seekNextPage ()) return false;
1040 #ifdef DEBUG_INTERNALGENERATOR
1041 m_device->debug ("!!!! InternalGenerator: Write pageLayout; page=", m_device->tellInternal () / 128);
1042 #endif
1043 m_pageLayout->setDevice (m_device);
1044 m_pageLayout->setHeader (m_header);
1045 if (!m_pageLayout->writeToDevice ()) return false;
1046
1047 #ifdef DEBUG_INTERNALGENERATOR
1048 m_device->debug ("!!!! InternalGenerator: Write sectionTable\n");
1049 #endif
1050 if (!seekNextPage ()) return false;
1051 m_sectionTable->setDevice (m_device);
1052 m_sectionTable->setHeader (m_header);
1053 if (!m_sectionTable->writeToDevice (m_pageLayout->getIsModified ())) return false;
1054
1055 #ifdef DEBUG_INTERNALGENERATOR
1056 m_device->debug ("!!!! InternalGenerator: Write pageTable\n");
1057 #endif
1058 if (!seekNextPage ()) return false;
1059 m_pageTable->setDevice (m_device);
1060 m_pageTable->setHeader (m_header);
1061 m_pageTable->setPageNumberStart (m_pageLayout->getPageNumberStart ());
1062 if (!m_pageTable->writeToDevice ()) return false;
1063
1064 #ifdef DEBUG_INTERNALGENERATOR
1065 m_device->debug ("!!!! InternalGenerator: Write fontTable\n");
1066 #endif
1067 if (!seekNextPage ()) return false;
1068 m_fontTable->setDevice (m_device);
1069 m_fontTable->setHeader (m_header);
1070 if (!m_fontTable->writeToDevice ()) return false;
1071
1072 #ifdef DEBUG_INTERNALGENERATOR
1073 m_device->debug ("!!!! InternalGenerator: Write header\n");
1074 #endif
1075 // write header
1076 m_header->setFormat (format);
1077 m_header->setNumPages ((m_device->tellInternal () + 127) / 128);
1078 m_header->setDevice (m_device);
1079 if (!m_device->seekInternal (0 * 128, SEEK_SET)) return false;
1080 if (!m_header->writeToDevice ()) return false;
1081
1082 // pad up to 128 (it seems that the ms programmers really did expect 128-byte pages)
1083 // BTW, if you look in a Write file, you can normally see some garbage after the
1084 // fontTable that appears to be part of an earlier part of the document
1085 // (or even some _other_ document!) suggesting that ms really did reuse their memory
1086 // and that we shouldn't be all that concerned with "unknown", "reserved" and "zero"
1087 // fields
1088 if (!m_device->seekInternal (m_header->getNumPages () * 128, SEEK_SET)) return false;
1089
1090 return true;
1091 }
1092
1093 bool InternalGenerator::writeParaInfoBegin (const FormatParaProperty * /*paraProperty*/,
1094 const OLE *ole,
1095 const Image *image)
1096 {
1097 #ifdef DEBUG_INTERNALGENERATOR
1098 m_device->debug ("!!!! InternalGenerator: writeParaInfoBegin\n");
1099 #endif
1100
1101 if (ole)
1102 {
1103 m_ole = new OLE;
1104 if (!m_ole)
1105 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for OLE\n");
1106 *m_ole = *ole;
1107
1108 m_ole->setDevice (m_device);
1109 m_ole->setExternalObjectSize (ole->getExternalObjectSize ());
1110 }
1111
1112 if (image)
1113 {
1114 m_image = new Image;
1115 if (!m_image)
1116 ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for image\n");
1117 *m_image = *image;
1118
1119 m_image->setDevice (m_device);
1120 m_image->setExternalImageSize (image->getExternalImageSize ());
1121 }
1122
1123 return true;
1124 }
1125
1126 bool InternalGenerator::writeParaInfoEnd (const FormatParaProperty *paraProperty,
1127 const OLE */*ole*/,
1128 const Image */*image*/)
1129 {
1130 #ifdef DEBUG_INTERNALGENERATOR
1131 m_device->debug ("!!!! InternalGenerator: writeParaInfoEnd\n");
1132 #endif
1133
1134 if (m_ole)
1135 {
1136 if (!m_ole->writeToDevice ())
1137 return false;
1138
1139 delete m_ole;
1140 m_ole = NULL;
1141 }
1142 else if (m_image)
1143 {
1144 if (!m_image->writeToDevice ())
1145 return false;
1146
1147 delete m_image;
1148 m_image = NULL;
1149 }
1150
1151 m_paragraphInfo->setMargins (m_pageLayout->getLeftMargin (), m_pageLayout->getRightMargin ());
1152 return m_paragraphInfo->add (paraProperty, false/* don't force */);
1153 }
1154
1155 bool InternalGenerator::writeCharInfoBegin (const FormatCharProperty * /*charProperty*/)
1156 {
1157 #ifdef DEBUG_INTERNALGENERATOR
1158 m_device->debug ("!!!! InternalGenerator: writeCharInfoBegin\n");
1159 #endif
1160
1161 return true;
1162 }
1163
1164 bool InternalGenerator::writeCharInfoEnd (const FormatCharProperty *charProperty,
1165 const bool)
1166 {
1167 #ifdef DEBUG_INTERNALGENERATOR
1168 m_device->debug ("!!!! InternalGenerator: writeCharInfoEnd\n");
1169 #endif
1170
1171 m_characterInfo->setFontTable (m_fontTable);
1172 return m_characterInfo->add (charProperty, false/* don't force */);
1173 }
1174
1175 bool InternalGenerator::writeBinary (const Byte *buffer, const DWord length)
1176 {
1177 if (m_ole)
1178 {
1179 if (!m_ole->setExternalObject (buffer, length))
1180 return false;
1181 }
1182 else if (m_image)
1183 {
1184 if (!m_image->setExternalImage (buffer, length))
1185 return false;
1186 }
1187 else
1188 ErrorAndQuit (Error::InternalError, "attempt to write unknown type of binary data\n");
1189
1190 return true;
1191 }
1192
1193 bool InternalGenerator::writeText (const Byte *string)
1194 {
1195 #ifdef DEBUG_INTERNALGENERATOR
1196// m_device->debug ("!!!! InternalGenerator: writeText> ", (const char *) string);
1197 #endif
1198
1199 DWord length = DWord (strlen ((const char *) string));
1200
1201 if (!m_device->writeInternal (string, length)) return false;
1202
1203 return true;
1204 }
1205
1206 bool InternalGenerator::writePageNew (const int pageNumberClaimed)
1207 {
1208 #ifdef DEBUG_INTERNALGENERATOR
1209 m_device->debug ("!!!! InternalGenerator: writePageNew() with pageNo ",
1210 pageNumberClaimed);
1211 #endif
1212
1213 // is this a forced new page (as in, the signal was only generated because I put it in the spec?)
1214 if (pageNumberClaimed == 0)
1215 return true; // no real pageTable
1216
1217 PagePointer pp;
1218 pp.setPageNumber (pageNumberClaimed);
1219 pp.setFirstCharByte (m_device->tellInternal () - 128);
1220
1221 return m_pageTable->add (&pp);
1222 }
1223
1224} // namespace MSWrite {
1225
1226// end of libmswrite.cpp