/* This file is part of the LibMSWrite Library Copyright (C) 2001-2003 Clarence Dang This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License Version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License Version 2 for more details. You should have received a copy of the GNU Library General Public License Version 2 along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. LibMSWrite Project Website: http://sourceforge.net/projects/libmswrite/ */ /* * structures_private.cpp - Process Internal Microsoft(r) Write file structures * - that are hidden/abstracted away from the user * This file has ugly reading/writing code that couldn't be generated. * It performs some higher-level sanity checks. * * Don't use any of these classes as it would defeat the purpose of LibMSWrite. */ #include "structures_private.h" #include "structures.h" namespace MSWrite { Header::Header () { } Header::~Header () { } Header &Header::operator= (const Header &rhs) { if (this == &rhs) return *this; HeaderGenerated::operator= (rhs); m_numCharBytes = rhs.m_numCharBytes; m_pageCharInfo = rhs.m_pageCharInfo; return *this; } bool Header::readFromDevice (void) { CHECK_DEVICE; #ifdef DEBUG_HEADER m_device->debug ("\n<<<< Header::readFromDevice >>>>\n"); #endif if (!m_device->seekInternal (0, SEEK_SET)) return false; if (!HeaderGenerated::readFromDevice ()) return false; #ifdef DEBUG_HEADER switch (m_magic) { case 0xBE31: m_device->debug ("normal write file\n"); break; case 0xBE32: m_device->debug ("write file with OLE\n"); break; default: ErrorAndQuit (Error::InternalError, "magic test passed but failed later?"); } #endif m_numCharBytes = m_numCharBytesPlus128 - 128; #ifdef DEBUG_HEADER m_device->debug ("num bytes of data (text+images+OLE): ", m_numCharBytes); m_device->debug ("page # start of parainfo: ", m_pageParaInfo); m_device->debug ("page # footnote table: ", m_pageFootnoteTable); m_device->debug ("page # pageLayout: ", m_pageSectionProperty); m_device->debug ("page # sectionTable: ", m_pageSectionTable); m_device->debug ("page # pageTable: ", m_pagePageTable); m_device->debug ("page # fontTable: ", m_pageFontTable); m_device->debug ("num pages in file: ", m_numPages); #endif /* * more checks */ // footnoteTables simply do not exist... if (m_pageFootnoteTable != m_pageSectionProperty) ErrorAndQuit (Error::InvalidFormat, "document should not have a footnoteTable\n"); // cannot have a sectionProperty without a sectionTable or vice-versa // no sectionProperty? if (m_pageSectionProperty == m_pagePageTable) { // must not have a sectionTable then if (m_pageSectionTable != m_pagePageTable) ErrorAndQuit (Error::InvalidFormat, "sectionTable without sectionProperty\n"); } // have sectionProperty... else { if (m_pageSectionTable != m_pageSectionProperty + 1) ErrorAndQuit (Error::InvalidFormat, "sectionTable not immediately after sectionProperty\n"); if (m_pageSectionTable == m_pagePageTable) ErrorAndQuit (Error::InvalidFormat, "sectionProperty without sectionTable\n"); } // charInfo page comes immediately after the header and text m_pageCharInfo = (m_numCharBytesPlus128 + 127) / 128; #ifdef DEBUG_HEADER m_device->debug ("page # start of charinfo: ", m_pageCharInfo); #endif // catches a lot of corrupted files if (m_pageCharInfo > m_pageParaInfo) ErrorAndQuit (Error::InvalidFormat, "charInfo page after paraInfo page\n"); return true; } bool Header::writeToDevice (void) { CHECK_DEVICE; #ifdef DEBUG_HEADER m_device->debug ("\n<<<< Header::writeToDevice >>>>\n"); #endif m_numCharBytesPlus128 = m_numCharBytes + 128; if (!m_device->seekInternal (0, SEEK_SET)) return false; if (!HeaderGenerated::writeToDevice ()) return false; return true; } SectionDescriptor::SectionDescriptor () { } SectionDescriptor::~SectionDescriptor () { } SectionDescriptor &SectionDescriptor::operator= (const SectionDescriptor &rhs) { if (this == &rhs) return *this; SectionDescriptorGenerated::operator= (rhs); return *this; } SectionTable::SectionTable () { } SectionTable::~SectionTable () { } SectionTable &SectionTable::operator= (const SectionTable &rhs) { if (this == &rhs) return *this; SectionTableGenerated::operator= (rhs); NeedsHeader::operator= (rhs); return *this; } // this function really only does some sanity checking // in fact, a SectionTable is quite useless in Write... // ...so you actually don't have to call it :) bool SectionTable::readFromDevice (void) { CHECK_DEVICE; #ifdef DEBUG_PAGELAYOUT m_device->debug ("\n<<<< SectionTable::readFromDevice >>>>\n"); #endif int numSectionTablePages = m_header->getNumPageSectionTable (); #ifdef DEBUG_PAGELAYOUT m_device->debug ("num sectionTablePages=", numSectionTablePages); #endif // no SectionTable if (numSectionTablePages == 0) return true; else if (numSectionTablePages > 1) ErrorAndQuit (Error::InvalidFormat, "invalid #sectionTablePages\n"); // seek to the SectionTable in the file if (!m_device->seekInternal (m_header->getPageSectionTable () * 128, SEEK_SET)) return false; if (!SectionTableGenerated::readFromDevice ()) return false; #ifdef DEBUG_PAGELAYOUT m_device->debug ("num sectionDescriptors=", m_numSectionDescriptors); for (int i = 0; i < 2; i++) { m_device->debug ("Dumping SED #", i); m_device->debug ("\tbyte after section=", m_sed [i]->getAfterEndCharByte ()); m_device->debug ("\tsectionProperty location=", m_sed [i]->getSectionPropertyLocation ()); } #endif if (m_numSectionDescriptors != 2) m_device->error (Error::Warn, "#sectionDescriptors != 2, ignoring"); // check that 1st SectionDescriptor covers all characters if (m_sed [0]->getAfterEndCharByte () != m_header->getNumCharBytes ()) m_device->error (Error::Warn, "sectionDescriptor #1 does not cover entire document\n"); // check that 1st SectionDescriptor points to the SectionProperty if (m_sed [0]->getSectionPropertyLocation () != m_header->getPageSectionProperty () * DWord (128)) m_device->error (Error::Warn, "sectionDescriptor #1 does not refer to correct sectionProperty, ignoring\n"); // check that 2nd SectionDescriptor covers after the end of the document if (m_sed [1]->getAfterEndCharByte () != m_header->getNumCharBytes () + 1) m_device->error (Error::Warn, "sectionDescriptor #2 does not cover post-document\n"); // check that 2nd SectionDescriptor is indeed a dummy if (m_sed [1]->getSectionPropertyLocation () != (DWord) 0xFFFFFFFF) m_device->error (Error::Warn, "sectionDescriptor #2 is not a dummy\n"); return true; } bool SectionTable::writeToDevice (const bool needed) { CHECK_DEVICE; #ifdef DEBUG_PAGELAYOUT m_device->debug ("\n>>>> SectionTable::writeToDevice <<<<\n"); #endif m_header->setPageSectionTable (m_device->tellInternal () / 128); // if no sectionProperty, no sectionTable if (!needed) return true; // set that 1st SectionDescriptor covers all characters m_sed [0]->setAfterEndCharByte (m_header->getNumCharBytes ()); // set that 1st SectionDescriptor points to the SectionProperty m_sed [0]->setSectionPropertyLocation (m_header->getPageSectionProperty () * 128); // set that 2nd SectionDescriptor covers after the end of the document m_sed [1]->setAfterEndCharByte (m_header->getNumCharBytes () + 1); // set that 2nd SectionDescriptor is indeed a dummy m_sed [1]->setSectionPropertyLocation ((DWord) 0xFFFFFFFF); if (!SectionTableGenerated::writeToDevice ()) return false; return true; } FontTable::FontTable () { } FontTable::~FontTable () { } FontTable &FontTable::operator= (const FontTable &rhs) { if (this == &rhs) return *this; FontTableGenerated::operator= (rhs); NeedsHeader::operator= (rhs); m_fontList = rhs.m_fontList; return *this; } bool FontTable::readFromDevice (void) { CHECK_DEVICE; #ifdef DEBUG_FONT m_device->debug ("\n<<<< FontTable::readFromDevice >>>>\n"); #endif int numFontTablePages = m_header->getNumPageFontTable (); // no FontTable if (numFontTablePages == 0) return true; // seek to the fontTable in the file if (!m_device->seekInternal (m_header->getPageFontTable () * 128, SEEK_SET)) return false; if (!FontTableGenerated::readFromDevice ()) return false; #ifdef DEBUG_FONT m_device->debug ("num Fonts: ", m_numFonts); #endif bool dontAddToBack = false; for (int i = 0; i < FontTableGenerated::m_numFonts; i++) { #ifdef DEBUG_FONT m_device->debug ("Font: ", i); #endif if (!dontAddToBack) { if (!m_fontList.addToBack ()) ErrorAndQuit (Error::OutOfMemory, "could not add Font to fontList\n"); } else dontAddToBack = false; List ::Iterator it = m_fontList.begin (false); Font &font = *it; // read font font.setDevice (m_device); if (!font.readFromDevice ()) { // a real error... if (m_device->bad ()) return false; // // ... or Font::readFromDevice() signalled something // // font is on next page? if (font.getNumDataBytes () == 0xFFFF) { #ifdef DEBUG_FONT m_device->debug ("\tcurrent offset=", m_device->tellInternal ()); m_device->debug ("\tnext page offset=", (m_device->tellInternal () + 127) / 128 * 128); #endif // seek to next page if (!m_device->seekInternal ((m_device->tellInternal () + 127) / 128 * 128, SEEK_SET)) return false; --i; // still reading the same font dontAddToBack = true; // don't need to add another font, just re-use this sentinel continue; } // no more entries (should _not_ get in here)? if (font.getNumDataBytes () == 0) { if (i != m_numFonts - 1) // actually, this check isn't required m_device->error (Error::Warn, "font marked as last but is not\n"); // let's not cause problems later with uninitialised Font::m_name's m_fontList.erase (it); // better quit just in case (it is better to have fewer fonts than risk errors) break; } } } // for (int i = 0; i < m_numFonts; i++) { return true; } bool FontTable::writeToDevice (void) { CHECK_DEVICE; #ifdef DEBUG_FONT m_device->debug ("\n>>>> FontTable::writeToDevice <<<<\n"); #endif // indicate fontTable page in header m_header->setPageFontTable (m_device->tellInternal () / 128); FontTableGenerated::m_numFonts = m_fontList.getNumElements (); // no fontTable if (FontTableGenerated::m_numFonts == 0) { m_device->error (Error::Warn, "not writing fontTable\n"); return true; } // write numFonts if (!FontTableGenerated::writeToDevice ()) return false; int i = 0; for (List ::Iterator it = m_fontList.begin (); it != m_fontList.end (); /* don't iterate here since we might "continue" with the same font */) { #ifdef DEBUG_FONT m_device->debug ("\twriting font #", i); #endif Font &font = *it; // write font font.setDevice (m_device); if (!font.writeToDevice ()) { // a real error... if (m_device->bad ()) return false; // // ... or Font::readFromDevice() signalled something // // font is on next page then #ifdef DEBUG_FONT m_device->debug ("\tcurrent offset=", m_device->tellInternal ()); m_device->debug ("\tnext page offset=", (m_device->tellInternal () + 127) / 128 * 128); #endif // seek to next page if (!m_device->seekInternal ((m_device->tellInternal () + 127) / 128 * 128, SEEK_SET)) return false; // still writing the same font continue; } // get next font to write it++; i++; } return true; } Font *FontTable::getFont (const DWord fontCode) const { // OPT: inefficient, maybe an array of pointers because we can't delete fonts anyway List ::Iterator it = m_fontList.begin (); for (int i = 0; i < int (fontCode) && it != m_fontList.end (); i++, ++it) ; if (it == m_fontList.end ()) return NULL; return &(*it); } DWord FontTable::findFont (const Font *want) const { DWord code = 0; List ::Iterator it; for (it = m_fontList.begin (); it != m_fontList.end (); ++it) { if ((*it) == *want) return code; code++; } // couldn't find return 0xFFFFFFFF; } DWord FontTable::addFont (const Font *input) { CHECK_DEVICE; #ifdef DEBUG_FONT m_device->debug ("\t\t\tTrying to add Font ", input->getName ()); #endif DWord e = findFont (input); // already in list if (e != 0xFFFFFFFF) { #ifdef DEBUG_FONT m_device->debug ("\t\t\t\tAlready in list, fontCode=", e); #endif // return fontCode return e; } // new font else { #ifdef DEBUG_FONT m_device->debug ("\t\t\t\tNew font, adding to list\n"); #endif if (!m_fontList.addToBack (*input)) ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for next font element\n"); return m_fontList.getNumElements () - 1; } } PagePointer::PagePointer () { } PagePointer::~PagePointer () { } PagePointer &PagePointer::operator= (const PagePointer &rhs) { if (this == &rhs) return *this; PagePointerGenerated::operator= (rhs); return *this; } bool PagePointer::readFromDevice (void) { CHECK_DEVICE; if (!PagePointerGenerated::readFromDevice ()) return false; #ifdef DEBUG_PAGETABLE m_device->debug ("\t\tpage number: ", m_pageNumber); m_device->debug ("\t\tfirst char: ", m_firstCharByte); #endif return true; } bool PagePointer::writeToDevice (void) { CHECK_DEVICE; if (!PagePointerGenerated::writeToDevice ()) return false; return true; } PageTable::PageTable () : m_pageNumberStart (0xFFFF) { } PageTable::~PageTable () { } PageTable &PageTable::operator= (const PageTable &rhs) { if (this == &rhs) return *this; PageTableGenerated::operator= (rhs); NeedsHeader::operator= (rhs); m_pagePointerList = rhs.m_pagePointerList; m_pageNumberStart = rhs.m_pageNumberStart; m_pageTableIterator = rhs.m_pageTableIterator; return *this; } bool PageTable::readFromDevice (void) { CHECK_DEVICE; #ifdef DEBUG_PAGETABLE m_device->debug ("\n<<<< PageTable::readFromDevice >>>>\n"); #endif #ifdef CHECK_INTERNAL if (m_pageNumberStart == (Word) 0xFFFF || NeedsHeader::m_header == NULL) ErrorAndQuit (Error::InternalError, "PageTable call not setup\n"); #endif // no pageTable if (m_header->getNumPagePageTable () == 0) return true; // seek to the pageTable in the file if (!m_device->seekInternal (m_header->getPagePageTable () * 128, SEEK_SET)) return false; if (!PageTableGenerated::readFromDevice ()) return false; #ifdef DEBUG_PAGETABLE Dump (numPagePointers); Dump (zero); #endif DWord lastCharByte = (DWord) 0xFFFFFFFF; Word lastPage = (Word) 0xFFFF; // do not have to init for (int i = 0; i < m_numPagePointers; i++) { if (!m_pagePointerList.addToBack ()) ErrorAndQuit (Error::OutOfMemory, "could not add pagePointer to list\n"); PagePointer &pp = *m_pagePointerList.begin (false); #ifdef DEBUG_PAGETABLE m_device->debug ("\tPagePointer: ", i); #endif // read pointer pp.setDevice (m_device); if (!pp.readFromDevice ()) return false; // first pointer? if (i == 0) { if (pp.getPageNumber () != m_pageNumberStart) ErrorAndQuit (Error::InvalidFormat, "pageTable & sectionProperty disagree on pageNumberStart\n"); } else { // according to KOffice 1.2: // // "the pageTable can get really out of sync with reality // if the user doesn't repaginate after deleting a few pages // -- so this is just a warning, not an error" // if (pp.getPageNumber () != lastPage + 1) m_device->error (Error::Warn, "pages don't follow each other\n"); if (pp.getFirstCharByte () <= lastCharByte) ErrorAndQuit (Error::InvalidFormat, "pageTable is not going forward?\n"); } // for checking, on next iteration lastPage = pp.getPageNumber (); lastCharByte = pp.getFirstCharByte (); } return true; } bool PageTable::writeToDevice (void) { CHECK_DEVICE; #ifdef DEBUG_PAGETABLE m_device->debug ("\n>>>> PageTable::writeToDevice <<<<\n"); #endif m_header->setPagePageTable (m_device->tellInternal () / 128); // LibMSWrite directly (!) adds to the list so m_numPagePointers may be out of date PageTableGenerated::m_numPagePointers = m_pagePointerList.getNumElements (); // no pageTable if (m_numPagePointers == 0) return true; // write numPagePointers, zero if (!PageTableGenerated::writeToDevice ()) return false; int i = 0; List ::Iterator it; for (it = m_pagePointerList.begin (); it != m_pagePointerList.end (); ++it, i++) { PagePointer &pp = (*it); #ifdef DEBUG_PAGETABLE m_device->debug ("\tPagePointer: ", i); #endif // read page pointer pp.setDevice (m_device); if (!pp.writeToDevice ()) return false; } return true; } FormatPointer::FormatPointer () : m_afterEndCharByte (0) { } FormatPointer::~FormatPointer () { } FormatPointer &FormatPointer::operator= (const FormatPointer &rhs) { if (this == &rhs) return *this; FormatPointerGenerated::operator= (rhs); m_afterEndCharByte = rhs.m_afterEndCharByte; m_formatProperty = rhs.m_formatProperty; return *this; } bool FormatPointer::readFromDevice (void) { CHECK_DEVICE; if (!FormatPointerGenerated::readFromDevice ()) return false; m_afterEndCharByte = m_afterEndCharBytePlus128 - 128; #ifdef DEBUG_FORMATINFO m_device->debug ("\t\tafterEndCharByte: ", m_afterEndCharByte); m_device->debug ("\t\tformatPropertyOffset: ", m_formatPropertyOffset); #endif return true; } bool FormatPointer::writeToDevice (void) { CHECK_DEVICE; #ifdef DEBUG_FORMATINFO m_device->debug ("\t\t\t\tafterEndCharByte: ", m_afterEndCharByte); m_device->debug ("\t\t\t\tformatPropertyOffset: ", m_formatPropertyOffset); #endif m_afterEndCharBytePlus128 = m_afterEndCharByte + 128; if (!FormatPointerGenerated::writeToDevice ()) return false; return true; } FormatInfoPage::FormatInfoPage () : m_firstCharByte (0), m_formatPointer (NULL), m_formatCharProperty (NULL), m_fontTable (NULL), m_formatParaProperty (NULL), m_leftMargin (0xFFFF), m_rightMargin (0xFFFF), m_formatPointerUpto (0), m_nextCharByte (0), m_formatPointerPos (0), m_formatPropertyPos (123) { // m_type= // m_lastPropertyOffset= // m_numProperty= } FormatInfoPage::~FormatInfoPage () { delete [] m_formatParaProperty; delete [] m_formatCharProperty; delete [] m_formatPointer; } bool FormatInfoPage::readFromDevice (void) { CHECK_DEVICE; #ifdef DEBUG_FORMATINFO if (m_type == ParaType) m_device->debug ("FormatInfoPage::readFromDevice (ParaType)\n"); else // if (m_type == CharType) m_device->debug ("FormatInfoPage::readFromDevice (CharType)\n"); #endif if (!FormatInfoPageGenerated::readFromDevice ()) return false; m_firstCharByte = FormatInfoPageGenerated::m_firstCharBytePlus128 - 128; #ifdef DEBUG_FORMATINFO m_device->debug ("number of FormatPointers on this page: ", FormatInfoPageGenerated::m_numFormatPointers); m_device->debug ("byte number of first character covered by this page: ", m_firstCharByte); #endif return true; } bool FormatInfoPage::writeToDevice (void) { CHECK_DEVICE; #ifdef DEBUG_FORMATINFO if (m_type == ParaType) m_device->debug ("FormatInfoPage::writeToDevice (ParaType)\n"); else // if (m_type == CharType) m_device->debug ("FormatInfoPage::writeToDevice (CharType)\n"); #endif FormatInfoPageGenerated::m_firstCharBytePlus128 = m_firstCharByte + 128; #ifdef DEBUG_FORMATINFO m_device->debug ("\tnumber of FormatPointers on this page: ", FormatInfoPageGenerated::m_numFormatPointers); m_device->debug ("\tbyte number of first character covered by this page: ", m_firstCharByte); #endif // will call FormatInfoPageGenerated::writeToArray()... // ...which will resolve to FormatInfoPage::writeToArray()... // which will then call the base FormatInfoPageGenerated::writeToArray() return FormatInfoPageGenerated::writeToDevice (); } bool FormatInfoPage::writeToArray (void) { CHECK_DEVICE; #ifdef DEBUG_FORMATINFO if (m_type == ParaType) m_device->debug ("\tFormatInfoPage::writeToArray (ParaType)\n"); else // if (m_type == CharType) m_device->debug ("\tFormatInfoPage::writeToArray (CharType)\n"); #endif MemoryDevice memdev; m_formatPointerPos = 0; m_formatPropertyPos = 123; #ifdef DEBUG_FORMATINFO m_device->debug ("\t\tWriting FormatPointer[], num=", m_numFormatPointers); #endif for (int i = 0; i < m_numFormatPointers; i++) { #ifdef DEBUG_FORMATINFO m_device->debug ("\t\t\tOffset=", m_formatPointerPos); #endif // write FormatPointer memdev.setCache (m_packedStructs + m_formatPointerPos); m_formatPointer [i].setDevice (&memdev); if (!m_formatPointer [i].writeToDevice ()) return false; memdev.setCache (NULL); m_formatPointerPos += FormatPointer::s_size; } #ifdef DEBUG_FORMATINFO m_device->debug ("\t\tWriting FormatProperty[], num=", m_numProperty); #endif for (int i = 0; i < m_numProperty; i++) { // write FormatProperty if (m_type == ParaType) { FormatParaProperty *prop = &m_formatParaProperty [i]; m_formatPropertyPos -= (sizeof (Byte) + prop->getNumDataBytes ()); memdev.setCache (m_packedStructs + m_formatPropertyPos); prop->setDevice (&memdev); // device was set to m_device to keep operator== happy in FormatInfoPage::add if (!prop->writeToDevice ()) return false; memdev.setCache (NULL); } else // if (m_type == CharType) { FormatCharProperty *prop = &m_formatCharProperty [i]; m_formatPropertyPos -= (sizeof (Byte) + prop->getNumDataBytes ()); memdev.setCache (m_packedStructs + m_formatPropertyPos); prop->setDevice (&memdev); // device was set to m_device to keep operator== happy in FormatInfoPage::add if (!prop->writeToDevice ()) return false; memdev.setCache (NULL); } #ifdef DEBUG_FORMATINFO m_device->debug ("\t\t\tWrote at Offset=", m_formatPropertyPos); #endif } if (!FormatInfoPageGenerated::writeToArray ()) return false; return true; } void *FormatInfoPage::begin (void) { CHECK_DEVICE; #ifdef DEBUG_FORMATINFO if (m_type == ParaType) m_device->debug ("\nFormatInfoPage::begin (ParaType)\n"); else // if (m_type == CharType) m_device->debug ("\nFormatInfoPage::begin (CharType)\n"); #endif m_formatPointerUpto = 0; m_nextCharByte = 0; // for consistency checking m_lastPropertyOffset = -1; if (!m_formatPointer) { m_formatPointer = new FormatPointer [1]; if (!m_formatPointer) { m_device->error (Error::OutOfMemory, "could not allocate memory for FormatPointer\n"); return NULL; } m_formatPointer->setDevice (m_device); } return this->next (); } void *FormatInfoPage::next (void) { void *ret; #ifdef DEBUG_FORMATINFO if (m_type == ParaType) m_device->debug ("\tPara FormatPointer #", m_formatPointerUpto); else // if (m_type == CharType) m_device->debug ("\tChar FormatPointer #", m_formatPointerUpto); #endif // // read Format Pointer // // check Format Pointer allocated if (!m_formatPointer) { m_device->error (Error::InternalError, "formatPointer not initialised - call FormatInfoPage::begin() before next()\n"); return NULL; } // read next Format Pointer (fixed length) m_device->setCache (m_packedStructs + m_formatPointerUpto * FormatPointer::s_size); if (!m_formatPointer->readFromDevice ()) return NULL; m_device->setCache (NULL); DWord formatPointerAfterEndCharByte = m_formatPointer->getAfterEndCharByte (); // check that it continues on from the last if (formatPointerAfterEndCharByte <= m_nextCharByte) m_device->error (Error::Warn, "FormatPointer afterEndCharByte does not go forward\n"); m_nextCharByte = formatPointerAfterEndCharByte; // check whether it is the last one (EOF-wise) if (formatPointerAfterEndCharByte >= m_header->getNumCharBytes ()) { #ifdef DEBUG_FORMATINFO m_device->debug ("\t\tThis is the last FormatPointer!\n"); #endif // past EOF? if (formatPointerAfterEndCharByte > m_header->getNumCharBytes ()) { m_device->error (Error::Warn, "FormatPointer ends after EOF, forcing it to end at EOF\n"); m_formatPointer->setAfterEndCharByte (m_header->getNumCharBytes ()); m_nextCharByte = m_header->getNumCharBytes (); } // but there are still more FormatPointers left? if (m_formatPointerUpto != m_numFormatPointers - 1) { m_device->error (Error::Warn, "FormatPointer ends at EOF but is not the last, forcing it to be the last\n"); m_formatPointerUpto = m_numFormatPointers - 1; // m_formatPointerUpto will get incremented at the end of this function } } // // read in corresponding Format Property // int currentPropertyOffset = m_formatPointer->getFormatPropertyOffset (); bool up2date = (currentPropertyOffset == m_lastPropertyOffset); if (!up2date) m_device->setCache (m_packedStructs + currentPropertyOffset); if (m_type == CharType) { if (!up2date) { // reset to defaults delete [] m_formatCharProperty; m_formatCharProperty = new FormatCharProperty [1]; if (!m_formatCharProperty) { m_device->error (Error::OutOfMemory, "could not allocate memory for FormatCharProperty\n"); m_device->setCache (NULL); return NULL; } m_formatCharProperty->setDevice (m_device); m_formatCharProperty->setFontTable (m_fontTable); if (!m_formatCharProperty->updateFont ()) { m_device->setCache (NULL); return NULL; } // corresponding charProperty structure exists? if (currentPropertyOffset != 0xFFFF) { if (!m_formatCharProperty->readFromDevice ()) { m_device->setCache (NULL); return NULL; } } } assert (m_formatCharProperty); m_formatCharProperty->setAfterEndCharByte (m_formatPointer->getAfterEndCharByte ()); ret = m_formatCharProperty; } else // if (m_type == ParaType) { if (!up2date) { // reset to defaults delete [] m_formatParaProperty; m_formatParaProperty = new FormatParaProperty [1]; if (!m_formatParaProperty) { m_device->error (Error::OutOfMemory, "could not allocate memory for FormatParaProperty\n"); m_device->setCache (NULL); return NULL; } m_formatParaProperty->setDevice (m_device); m_formatParaProperty->setMargins (m_leftMargin, m_rightMargin); // corresponding paraProperty structure exists? if (currentPropertyOffset != 0xFFFF) { if (!m_formatParaProperty->readFromDevice ()) { m_device->setCache (NULL); return NULL; } } } assert (m_formatParaProperty); m_formatParaProperty->setAfterEndCharByte (m_formatPointer->getAfterEndCharByte ()); ret = m_formatParaProperty; } if (!up2date) m_device->setCache (NULL); m_lastPropertyOffset = currentPropertyOffset; m_formatPointerUpto++; assert (m_formatPointer); return ret; } bool FormatInfoPage::end (void) const { assert (m_formatPointerUpto <= FormatInfoPageGenerated::m_numFormatPointers); return m_formatPointerUpto >= FormatInfoPageGenerated::m_numFormatPointers; } bool FormatInfoPage::add (const void *property) { CHECK_DEVICE; // MaxElements is true for FormatPointers since you can't have partial FormatPointers // This is also true for FormatProperties because m_numFormatPointer >= m_numProperty // so MaxElements is also the upper limit on the number of properties const int MaxElements = 123 / FormatPointer::s_size; #ifdef DEBUG_FORMATINFO if (m_type == ParaType) m_device->debug (">>>> FormatInfoPage::add (ParaType) <<<<\n"); else // if (m_type == CharType) m_device->debug (">>>> FormatInfoPage::add (CharType) <<<<\n"); Dump (formatPointerPos); Dump (formatPropertyPos); #endif // // Allocated memory for FormatPointers yet? // if (!m_formatPointer) { #ifdef DEBUG_FORMATINFO m_device->debug ("\tFIRST CALL! Allocating memory for FormatPointer[] and FormatProperty[]\n"); #endif // allocate memory for FormatPointers m_formatPointer = new FormatPointer [MaxElements + 1]; // lazy memory allocation (+1 in case we can merge the last one) if (!m_formatPointer) ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for formatPointer[]\n"); // don't need to use m_formatPointerUpto here FormatInfoPageGenerated::m_numFormatPointers = 0; // ok, better allocate memory for relevant FormatProperties if (m_type == ParaType) { assert (!m_formatParaProperty); m_formatParaProperty = new FormatParaProperty [MaxElements + 1]; // lazy memory allocation (+1 for default prop) m_formatParaProperty [MaxElements].setDevice (m_device); // we will be == this default which has to writeToArray which NeedsDevice if (!m_formatParaProperty) ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for formatParaProperty[]\n"); } else // if (m_type == CharType) { assert (!m_formatCharProperty); m_formatCharProperty = new FormatCharProperty [MaxElements + 1]; // lazy memory allocation (+1 for default prop) m_formatCharProperty [MaxElements].setDevice (m_device); // we will be == this default which has to writeToArray which NeedsDevice if (!m_formatCharProperty) ErrorAndQuit (Error::OutOfMemory, "could not allocate memory for formatCharProperty[]\n"); } m_numProperty = 0; } // // Setup pointers // FormatParaProperty inpp; FormatCharProperty incp; if (m_type == ParaType) { #ifdef DEBUG_FORMATINFO m_device->debug ("\tTrying to add ParaProperty, #numDataBytes=", ((FormatParaProperty *) property)->getNumDataBytes ()); #endif inpp = *((FormatParaProperty *) property); inpp.setDevice (m_device); inpp.setAfterEndCharByte (m_device->tellInternal () - 128); inpp.setMargins (m_leftMargin, m_rightMargin); if (!inpp.updateIndents ()) return false; } else // if (m_type == CharType) { #ifdef DEBUG_FORMATINFO m_device->debug ("\tTrying to add CharProperty, #numDataBytes=", ((FormatCharProperty *) property)->getNumDataBytes ()); #endif incp = *((FormatCharProperty *) property); incp.setDevice (m_device); incp.setAfterEndCharByte (m_device->tellInternal () - 128); incp.setFontTable (m_fontTable); if (!incp.updateFontCode ()) return false; } // // Attempt to add Property to current page: // // Since MS programmers tried so hard to save space, we will do the same :) // This results in having to store variable-length structures and the use of other nasty // tricks such as having multiple FormatPointers pointing to the same FormatProperty. // Details follow in code :) // // In Write, character formatting blocks can extend across paragraphs // In LibMSWrite they don't (for convenience?) so here we merge FormatPointers that have been split up // This applies _only_ to _character formatting_ and is not MS' fault but rather the design of LibMSWrite. // It should _not_ be done to paragraph formatting because I don't think the user would appreciate // several paragraphs being merged into one multi-line paragraph (changing paragraph properties would // become rather difficult...) bool checkedLastFormatPointer = false; if (m_type == CharType && m_numProperty > 0 /* && m_numFormatPointers > 0 */) { #ifdef DEBUG_FORMATINFO m_device->debug ("\tIs it the same property as last? "); #endif // simply extend last FormatPointer FormatCharProperty *lastProp = (FormatCharProperty *) m_formatPointer [m_numFormatPointers - 1].getFormatProperty (); if (incp == *lastProp) { #ifdef DEBUG_FORMATINFO m_device->debug ("Yes, extending FormatPointer #", m_numFormatPointers - 1); m_device->debug ("\t\tFrom ", m_formatPointer [m_numFormatPointers - 1].getAfterEndCharByte ()); #endif m_formatPointer [m_numFormatPointers - 1].setAfterEndCharByte (incp.getAfterEndCharByte ()); #ifdef DEBUG_FORMATINFO m_device->debug ("\t\tTo: ", incp.getAfterEndCharByte ()); #endif return true; } checkedLastFormatPointer = true; #ifdef DEBUG_FORMATINFO m_device->debug ("No\n"); #endif } #ifdef DEBUG_FORMATINFO m_device->debug ("\tSame as earlier FormatPointer? "); #endif // points to same property as an earlier formatPointer? for (int i = 0; i < m_numFormatPointers - (checkedLastFormatPointer ? 1 : 0); i++) { #ifdef DEBUG_FORMATINFO m_device->debug (i); m_device->debug ("? "); #endif if (m_type == ParaType) { FormatParaProperty *prop = (FormatParaProperty *) m_formatPointer [i].getFormatProperty (); if (inpp == *prop) { // try to add new formatPointer if (m_formatPointerPos + FormatPointer::s_size > m_formatPropertyPos) { #ifdef DEBUG_FORMATINFO m_device->debug ("Yes, but out of room on this page\n"); #endif return false; // not enough room } #ifdef DEBUG_FORMATINFO m_device->debug ("Yes, same property as ", i); #endif m_formatPointer [m_numFormatPointers].setAfterEndCharByte (inpp.getAfterEndCharByte ()); m_formatPointer [m_numFormatPointers].setFormatPropertyOffset (m_formatPointer [i].getFormatPropertyOffset ()); m_formatPointer [m_numFormatPointers].setFormatProperty (prop); m_formatPointerPos += FormatPointer::s_size; m_numFormatPointers++; return true; } } else // if (m_type == CharType) { FormatCharProperty *prop = (FormatCharProperty *) m_formatPointer [i].getFormatProperty (); if (incp == *prop) { // try to add new formatPointer if (m_formatPointerPos + FormatPointer::s_size > m_formatPropertyPos) { #ifdef DEBUG_FORMATINFO m_device->debug ("Yes, but out of room on this page\n"); #endif return false; // not enough room } #ifdef DEBUG_FORMATINFO m_device->debug ("Yes, same property as ", i); #endif m_formatPointer [m_numFormatPointers].setAfterEndCharByte (incp.getAfterEndCharByte ()); m_formatPointer [m_numFormatPointers].setFormatPropertyOffset (m_formatPointer [i].getFormatPropertyOffset ()); m_formatPointer [m_numFormatPointers].setFormatProperty (prop); m_formatPointerPos += FormatPointer::s_size; m_numFormatPointers++; return true; } } } #ifdef DEBUG_FORMATINFO m_device->debug ("No, outputting full FormatPointer & Property\n"); #endif // ok we can't compress, must output a full FormatPointer & FormatProperty if (m_type == ParaType) { int nextPropertyPos = m_formatPropertyPos; if (inpp.getNumDataBytes ()) nextPropertyPos -= (sizeof (Byte) + inpp.getNumDataBytes ()); if (m_formatPointerPos + FormatPointer::s_size > nextPropertyPos) { #ifdef DEBUG_FORMATINFO m_device->debug ("\tOut of room on this page!\n"); #endif return false; // not enough room } m_formatPropertyPos = nextPropertyPos; if (inpp.getNumDataBytes ()) m_formatParaProperty [m_numProperty] = inpp; m_formatPointer [m_numFormatPointers].setAfterEndCharByte (inpp.getAfterEndCharByte ()); if (inpp.getNumDataBytes ()) { m_formatPointer [m_numFormatPointers].setFormatPropertyOffset (m_formatPropertyPos); m_formatPointer [m_numFormatPointers].setFormatProperty (&m_formatParaProperty [m_numProperty]); } else { m_formatPointer [m_numFormatPointers].setFormatPropertyOffset (0xFFFF/*nowhere*/); m_formatPointer [m_numFormatPointers].setFormatProperty (&m_formatParaProperty [MaxElements]/*default*/); } m_formatPointerPos += FormatPointer::s_size; if (inpp.getNumDataBytes ()) m_numProperty++; m_numFormatPointers++; return true; } else // if (m_type == CharType) { int nextPropertyPos = m_formatPropertyPos; if (incp.getNumDataBytes ()) nextPropertyPos -= (sizeof (Byte) + incp.getNumDataBytes ()); if (m_formatPointerPos + FormatPointer::s_size > nextPropertyPos) { #ifdef DEBUG_FORMATINFO m_device->debug ("\tOut of room on this page!\n"); #endif return false; // not enough room } m_formatPropertyPos = nextPropertyPos; if (incp.getNumDataBytes ()) m_formatCharProperty [m_numProperty] = incp; m_formatPointer [m_numFormatPointers].setAfterEndCharByte (incp.getAfterEndCharByte ()); if (incp.getNumDataBytes ()) { m_formatPointer [m_numFormatPointers].setFormatPropertyOffset (m_formatPropertyPos); m_formatPointer [m_numFormatPointers].setFormatProperty (&m_formatCharProperty [m_numProperty]); } else { m_formatPointer [m_numFormatPointers].setFormatPropertyOffset (0xFFFF/*nowhere*/); m_formatPointer [m_numFormatPointers].setFormatProperty (&m_formatCharProperty [MaxElements]/*default*/); } m_formatPointerPos += FormatPointer::s_size; if (incp.getNumDataBytes ()) m_numProperty++; m_numFormatPointers++; return true; } } BMP_BitmapFileHeader::BMP_BitmapFileHeader () { } BMP_BitmapFileHeader::~BMP_BitmapFileHeader () { } BMP_BitmapFileHeader &BMP_BitmapFileHeader::operator= (const BMP_BitmapFileHeader &rhs) { if (this == &rhs) return *this; BMP_BitmapFileHeaderGenerated::operator= (rhs); return *this; } bool BMP_BitmapFileHeader::readFromDevice (void) { CHECK_DEVICE; #ifdef DEBUG_IMAGE m_device->debug ("\n<<<< BMP_BitmapFileHeader::readFromDevice >>>>\n"); #endif if (!BMP_BitmapFileHeaderGenerated::readFromDevice ()) return false; #ifdef DEBUG_IMAGE Dump (magic); Dump (totalBytes); Dump (zero [0]); Dump (zero [1]); Dump (actualImageOffset); #endif return true; } bool BMP_BitmapFileHeader::writeToDevice (void) { CHECK_DEVICE; #ifdef DEBUG_IMAGE m_device->debug ("\n<<<< BMP_BitmapFileHeader::writeToDevice >>>>\n"); #endif if (!BMP_BitmapFileHeaderGenerated::writeToDevice ()) return false; return true; } BMP_BitmapInfoHeader::BMP_BitmapInfoHeader () { } BMP_BitmapInfoHeader::~BMP_BitmapInfoHeader () { } BMP_BitmapInfoHeader &BMP_BitmapInfoHeader::operator= (const BMP_BitmapInfoHeader &rhs) { if (this == &rhs) return *this; BMP_BitmapInfoHeaderGenerated::operator= (rhs); return *this; } bool BMP_BitmapInfoHeader::readFromDevice (void) { CHECK_DEVICE; #ifdef DEBUG_IMAGE m_device->debug ("\n<<<< BMP_BitmapInfoHeader::readFromDevice >>>>\n"); #endif if (!BMP_BitmapInfoHeaderGenerated::readFromDevice ()) return false; #ifdef DEBUG_IMAGE Dump (numHeaderBytes); Dump (width); Dump (height); Dump (numPlanes); Dump (bitsPerPixel); Dump (compression); Dump (sizeImage); Dump (xPixelsPerMeter); Dump (yPixelsPerMeter); Dump (coloursUsed); Dump (coloursImportant); #endif return true; } bool BMP_BitmapInfoHeader::writeToDevice (void) { CHECK_DEVICE; #ifdef DEBUG_IMAGE m_device->debug ("\n<<<< BMP_BitmapInfoHeader::writeToDevice >>>>\n"); #endif if (!BMP_BitmapInfoHeaderGenerated::writeToDevice ()) return false; return true; } BMP_BitmapColourIndex::BMP_BitmapColourIndex () { } BMP_BitmapColourIndex::~BMP_BitmapColourIndex () { } BMP_BitmapColourIndex &BMP_BitmapColourIndex::operator= (const BMP_BitmapColourIndex &rhs) { if (this == &rhs) return *this; BMP_BitmapColourIndexGenerated::operator= (rhs); return *this; } BitmapHeader::BitmapHeader () { } BitmapHeader::~BitmapHeader () { } BitmapHeader &BitmapHeader::operator= (const BitmapHeader &rhs) { if (this == &rhs) return *this; BitmapHeaderGenerated::operator= (rhs); return *this; } bool BitmapHeader::readFromDevice (void) { CHECK_DEVICE; #ifdef DEBUG_IMAGE m_device->debug ("\n<<<< BitmapHeader::readFromDevice >>>>\n"); #endif if (!BitmapHeaderGenerated::readFromDevice ()) return false; #ifdef DEBUG_IMAGE Dump (zero); Dump (width); Dump (height); Dump (widthBytes); Dump (numPlanes); Dump (bitsPerPixel); Dump (zero2); #endif return true; } bool BitmapHeader::writeToDevice (void) { CHECK_DEVICE; #ifdef DEBUG_IMAGE m_device->debug ("\n>>>> BitmapHeader::writeToDevice <<<<\n"); #endif if (!BitmapHeaderGenerated::writeToDevice ()) return false; return true; } WMFHeader::WMFHeader () { } WMFHeader::~WMFHeader () { } WMFHeader &WMFHeader::operator= (const WMFHeader &rhs) { if (this == &rhs) return *this; WMFHeaderGenerated::operator= (rhs); return *this; } bool WMFHeader::readFromDevice (void) { CHECK_DEVICE; #ifdef DEBUG_IMAGE m_device->debug ("\n<<<< WMFHeader::readFromDevice >>>>\n"); #endif if (!WMFHeaderGenerated::readFromDevice ()) return false; #ifdef DEBUG_IMAGE Dump (fieldType); Dump (headerSize); Dump (winVersion); Dump (fileSize); Dump (numObjects); Dump (maxRecordSize); Dump (zero); #endif return true; } bool WMFHeader::writeToDevice (void) { CHECK_DEVICE; #ifdef DEBUG_IMAGE m_device->debug ("\n>>>> WMFHeader::writeToDevice <<<<\n"); #endif #ifdef DEBUG_IMAGE Dump (fieldType); Dump (headerSize); Dump (winVersion); Dump (fileSize); Dump (numObjects); Dump (maxRecordSize); Dump (zero); #endif if (!WMFHeaderGenerated::writeToDevice ()) return false; return true; } } // namespace MSWrite { // end of structures_private.cpp