You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/filters/kword/mswrite/mswriteexport.cc

1976 lines
54 KiB

/* This file is part of the KDE project
Copyright (C) 2002-2003 Clarence Dang <dang@kde.org>
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.
*/
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tqbuffer.h>
#include <tqcstring.h>
#include <tqfile.h>
#include <tqfont.h>
#include <tqimage.h>
#include <tqtextcodec.h>
#include <tqvaluelist.h>
#include <tqvaluestack.h>
#include <kdebug.h>
#include <kgenericfactory.h>
#include <KoFilterChain.h>
#include <kowmfpaint.h>
#include <KWEFStructures.h>
#include <KWEFBaseWorker.h>
#include <KWEFKWordLeader.h>
#include "libmswrite.h"
#include "mswriteexport.h"
class MSWriteExportFactory : KGenericFactory <MSWriteExport, KoFilter>
{
public:
MSWriteExportFactory () : KGenericFactory <MSWriteExport, KoFilter> ("kwordmswriteexport")
{
}
protected:
virtual void setupTranslations (void)
{
TDEGlobal::locale()->insertCatalogue ("kofficefilters");
}
};
K_EXPORT_COMPONENT_FACTORY (libmswriteexport, MSWriteExportFactory ())
class WRIDevice : public MSWrite::Device
{
private:
FILE *m_outfp;
long m_outfp_pos, m_outfp_eof;
public:
WRIDevice () : m_outfp (NULL), m_outfp_pos (0), m_outfp_eof (0)
{
}
virtual ~WRIDevice ()
{
closeFile ();
}
bool openFile (const char *fileName)
{
m_outfp = fopen (fileName, "wb");
if (!m_outfp)
{
error (MSWrite::Error::FileError, "could not open file for writing\n");
return false;
}
return true;
}
bool closeFile (void)
{
if (m_outfp)
{
if (fclose (m_outfp))
{
error (MSWrite::Error::FileError, "could not close output file\n");
return false;
}
m_outfp = NULL;
}
return true;
}
bool read (MSWrite::Byte *, const MSWrite::DWord)
{
error (MSWrite::Error::InternalError, "reading from an output file?\n");
return false;
}
bool write (const MSWrite::Byte *buf, const MSWrite::DWord numBytes)
{
size_t cwrite = fwrite (buf, 1, numBytes, m_outfp);
if (cwrite != numBytes)
{
error (MSWrite::Error::FileError, "could not write to output file\n");
return false;
}
// keep track of where we are up to in the output file and where EOF is
m_outfp_pos += numBytes;
if (m_outfp_pos > m_outfp_eof)
m_outfp_eof = m_outfp_pos;
return true;
}
bool seek (const long offset, const int whence)
{
long absloc;
switch (whence)
{
case SEEK_SET:
absloc = offset;
break;
case SEEK_CUR:
absloc = m_outfp_pos + offset;
break;
case SEEK_END:
absloc = m_outfp_eof + offset;
break;
default:
error (MSWrite::Error::InternalError, "invalid whence passed to WRIDevice::seek\n");
return false;
}
if (absloc > m_outfp_eof)
{
kdDebug (30509) << "Want to seek to " << absloc
<< " but EOF is at " << m_outfp_eof
<< "; so writing " << absloc - m_outfp_eof
<< " zeros" << endl;
if (fseek (m_outfp, m_outfp_eof, SEEK_SET))
{
error (MSWrite::Error::FileError,
"could not seek to EOF in output file\n");
return false;
}
MSWrite::Byte *zero = new MSWrite::Byte [absloc - m_outfp_eof];
if (!zero)
{
error (MSWrite::Error::OutOfMemory,
"could not allocate memory for zeros\n");
return false;
}
memset (zero, 0, absloc - m_outfp_eof);
if (!write (zero, absloc - m_outfp_eof)) return false;
delete [] zero;
m_outfp_eof = absloc;
m_outfp_pos = absloc;
return true;
}
else
{
if (fseek (m_outfp, offset, whence) == 0)
{
m_outfp_pos = absloc;
return true;
}
else
{
error (MSWrite::Error::FileError, "could not seek output file\n");
return false;
}
}
}
long tell (void)
{
return ftell (m_outfp);
}
void debug (const char *s)
{
kdDebug (30509) << s;
}
void debug (const int i)
{
kdDebug (30509) << i;
}
void error (const int errorCode, const char *message,
const char * /*file*/ = "", const int /*lineno*/ = 0,
MSWrite::DWord /*token*/ = MSWrite::Device::NoToken)
{
if (errorCode == MSWrite::Error::Warn)
kdWarning (30509) << message;
else
{
m_error = errorCode;
kdError (30509) << message;
}
}
};
class KWordMSWriteWorker : public KWEFBaseWorker
{
private:
WRIDevice *m_device;
MSWrite::InternalGenerator *m_generator;
MSWrite::PageLayout m_pageLayout;
MSWrite::Word m_pageHeight, m_pageWidth,
m_topMargin, m_leftMargin, m_bottomMargin, m_rightMargin;
MSWrite::Word m_pageNumberStart;
// for charset conversion
TQTextCodec *m_codec;
TQTextEncoder *m_encoder;
TQValueList <HeaderData> m_headerData;
TQValueList <FooterData> m_footerData;
int m_headerType, m_footerType;
bool m_hasHeader, m_isHeaderOnFirstPage;
bool m_hasFooter, m_isFooterOnFirstPage;
enum inWhatPossiblities
{
Nothing,
Header,
Footer,
Body
} m_inWhat;
public:
KWordMSWriteWorker () : m_device (NULL), m_generator (NULL),
m_pageHeight (0xFFFF), m_pageWidth (0xFFFF),
m_topMargin (0xFFFF), m_leftMargin (0xFFFF),
m_bottomMargin (0xFFFF), m_rightMargin (0xFFFF),
m_encoder (NULL),
m_hasHeader (false), m_hasFooter (false),
m_inWhat (Nothing)
{
// just select windows-1252 until the "Select Encoding" dialog works
m_codec = TQTextCodec::codecForName ("CP 1252");
if (m_codec)
m_encoder = m_codec->makeEncoder();
else
kdWarning (30509) << "Cannot convert to Win Charset!" << endl;
m_device = new WRIDevice;
if (!m_device)
{
kdError (30509) << "Could not allocate memory for Device" << endl;
return;
}
m_generator = new MSWrite::InternalGenerator;
if (!m_generator)
{
m_device->error (MSWrite::Error::OutOfMemory, "could not allocate memory for InternalGenerator\n");
return;
}
m_generator->setDevice (m_device);
}
virtual ~KWordMSWriteWorker ()
{
delete m_generator;
delete m_device;
delete m_encoder;
}
int getError (void) const
{
return m_device->bad ();
}
bool doOpenFile (const TQString &outFileName, const TQString &)
{
// constructor failed?
if (!m_device || !m_generator)
return false;
if (!m_device->openFile (TQFile::encodeName (outFileName))) return false;
return true;
}
bool doCloseFile (void)
{
if (!m_device->closeFile ()) return false;
return true;
}
bool doOpenDocument (void)
{
kdDebug (30509) << "doOpenDocument ()" << endl;
// We can't open the document here because we don't yet have
// PageLayout * as doFullPaperFormat() and doFullPaperBorders()
// haven't been called yet.
//
// doTrulyOpenDocument truly opens the document and is called by
// doOpenBody()
return true;
}
bool doTrulyOpenDocument (void)
{
// TODO: Write's UI doesn't allow the user to change Height or Width so
// setting it here might not be a good idea...
m_pageLayout.setPageHeight (m_pageHeight);
m_pageLayout.setPageWidth (m_pageWidth);
m_pageLayout.setPageNumberStart (m_pageNumberStart);
m_pageLayout.setTopMargin (m_topMargin);
m_pageLayout.setLeftMargin (m_leftMargin);
m_pageLayout.setTextHeight (m_pageHeight - m_topMargin - m_bottomMargin);
m_pageLayout.setTextWidth (m_pageWidth - m_leftMargin - m_rightMargin);
// TODO: libexport
// headerFromTop
// footerFromTop
if (!m_generator->writeDocumentBegin (MSWrite::Format::Write_3_0,
&m_pageLayout)) return false;
return true;
}
bool doCloseDocument (void)
{
kdDebug (30509) << "doCloseDocument ()" << endl;
if (!m_generator->writeDocumentEnd (MSWrite::Format::Write_3_0,
&m_pageLayout)) return false;
return true;
}
bool doFullPaperFormat (const int format,
const double width, const double height,
const int orientation)
{
kdDebug (30509) << "doFullPaperFormat ("
<< format << ", "
<< width << ", "
<< height << ", "
<< orientation << ")" << endl;
// TODO: does "format" or "orientation" matter?
m_pageHeight = MSWrite::Word (Point2Twip (height));
m_pageWidth = MSWrite::Word (Point2Twip (width));
return true;
}
bool doFullPaperBorders (const double top, const double left,
const double bottom, const double right)
{
kdDebug (30509) << "doFullPaperBorders ("
<< top << ", "
<< left << ", "
<< bottom << ", "
<< right << ")" << endl;
m_topMargin = MSWrite::Word (Point2Twip (top));
m_leftMargin = MSWrite::Word (Point2Twip (left));
m_bottomMargin = MSWrite::Word (Point2Twip (bottom));
m_rightMargin = MSWrite::Word (Point2Twip (right));
return true;
}
bool doVariableSettings (const VariableSettingsData &varSettings)
{
m_pageNumberStart = MSWrite::Word (varSettings.startingPageNumber);
kdDebug (30509) << "doVariableSettings pageNumberStart="
<< m_pageNumberStart << endl;
return true;
}
// In Write the header/footer must be the same for every page except that
// you can choose to not display it on the first page
//
// /*This filter aims to be as lossless as possible so if we can't
// accommodate the types of headers/footers found in KWord, we at least
// print out the paragraphs in the body*/
//
// Not anymore. Dumping headers & footers in the body didn't
// turn out to be that useful, nor was it expected by users.
//
bool doPageInfo (int headerType, int footerType)
{
kdDebug (30509) << "doPageInfo (headerType=" << headerType
<< ", footerType=" << footerType
<< ")" << endl;
m_headerType = headerType;
switch (headerType)
{
case 0: // same on all pages
case 3: // different on even and odd pages
m_isHeaderOnFirstPage = true;
break;
case 1: // different on first, even and odd pages
case 2: // different on first and other pages
m_isHeaderOnFirstPage = false;
break;
default:
kdWarning (30509) << "Unknown headerType: " << headerType << endl;
m_isHeaderOnFirstPage = false; // just a guess
break;
}
m_footerType = footerType;
switch (footerType)
{
case 0: // same on all pages
case 3: // different on even and odd pages
m_isFooterOnFirstPage = true;
break;
case 1: // different on first, even and odd pages
case 2: // different on first and other pages
m_isFooterOnFirstPage = false;
break;
default:
kdWarning (30590) << "Unknown footerType: " << footerType << endl;
m_isFooterOnFirstPage = false; // just a guess
break;
}
return true;
}
bool isParaListEmpty (const TQValueList <ParaData> &para)
{
if (para.count () == 1)
{
if (para.first ().text.isEmpty ())
return true;
}
return false;
}
bool doHeader (const HeaderData &header)
{
kdDebug (30509) << "doHeader (header.page=" << header.page << ")" << endl;
if (isParaListEmpty (header.para))
{
kdDebug (30509) << "\tEmpty, ignoring" << endl;
return true;
}
#if 0
switch (m_headerType)
{
case 0: // same on all pages
if (header.page != HeaderData::PAGE_ALL)
return true;
break;
case 3: // different on even and odd pages
if (header.page != HeaderData::PAGE_ODD &&
header.page != HeaderData::PAGE_EVEN)
return true;
break;
case 1: // different on first, even and odd pages
// accept everything
break;
case 2: // different on first and other pages
if (header.page != HeaderData::PAGE_FIRST &&
header.page != HeaderData::PAGE_ODD)
return true;
break;
}
#endif
m_hasHeader = true;
m_headerData.push_back (header);
return true;
}
bool doFooter (const FooterData &footer)
{
kdDebug (30509) << "doFooter (footer.page=" << footer.page << ")" << endl;
if (isParaListEmpty (footer.para))
{
kdDebug (30509) << "\tEmpty, ignoring" << endl;
return true;
}
#if 0
switch (m_footerType)
{
case 0: // same on all pages
if (footer.page != FooterData::PAGE_ALL)
return true;
break;
case 3: // different on even and odd pages
if (footer.page != FooterData::PAGE_ODD &&
footer.page != FooterData::PAGE_EVEN)
return true;
break;
case 1: // different on first, even and odd pages
// accept everything
break;
case 2: // different on first and other pages
if (footer.page != FooterData::PAGE_FIRST &&
footer.page != FooterData::PAGE_ODD)
return true;
break;
}
#endif
m_hasFooter = true;
m_footerData.push_back (footer);
return true;
}
bool doOpenBody (void)
{
kdDebug (30509) << "doOpenBody ()" << endl;
//
// Document Start
//
if (!doTrulyOpenDocument ()) return false;
//
// Footers followed by Headers (in this order)
//
bool wroteFooter = false;
m_inWhat = Footer;
for (TQValueList <FooterData>::Iterator it = m_footerData.begin ();
it != m_footerData.end ();
it++)
{
if ((*it).page != FooterData::PAGE_FIRST)
{
if (!wroteFooter)
{
if (!m_generator->writeFooterBegin ()) return false;
wroteFooter = true;
}
if (!doFullParagraphList ((*it).para)) return false;
it = --m_footerData.erase (it);
}
}
if (wroteFooter)
if (!m_generator->writeFooterEnd ()) return false;
bool wroteHeader = false;
m_inWhat = Header;
for (TQValueList <HeaderData>::Iterator it = m_headerData.begin ();
it != m_headerData.end ();
it++)
{
if ((*it).page != HeaderData::PAGE_FIRST)
{
if (!wroteHeader)
{
if (!m_generator->writeHeaderBegin ()) return false;
wroteHeader = true;
}
if (!doFullParagraphList ((*it).para)) return false;
it = --m_headerData.erase (it);
}
}
if (wroteHeader)
if (!m_generator->writeHeaderEnd ()) return false;
//
// Body Start
//
m_inWhat = Body;
if (!m_generator->writeBodyBegin ()) return false;
// KWord doesn't have a PageTable but we must emit the pageNew
// signal at least once
if (!m_generator->writePageNew ()) return false;
#if 0
// dump remaining header paragraphs at the start of the body
for (TQValueList <HeaderData>::Iterator it = m_headerData.begin ();
it != m_headerData.end ();
it++)
{
kdDebug (30509) << "BODY START ADDING HEADER: " << (*it).page << endl;
if (!doFullParagraphList ((*it).para)) return false;
it = --m_headerData.erase (it);
}
// dump remaining footer paragraphs too
for (TQValueList <FooterData>::Iterator it = m_footerData.begin ();
it != m_footerData.end ();
it++)
{
kdDebug (30509) << "BODY START ADDING FOOTER: " << (*it).page << endl;
if (!doFullParagraphList ((*it).para)) return false;
it = --m_footerData.erase (it);
}
#endif
return true;
}
bool doCloseBody (void)
{
kdDebug (30509) << "doCloseBody ()" << endl;
if (!m_generator->writeBodyEnd ()) return false;
return true;
}
// device that can either read from or write to a TQBuffer
// (but not both at the same time, please :))
class TQBufferDevice : public MSWrite::Device
{
private:
TQBuffer *m_buffer;
public:
TQBufferDevice (TQBuffer *buffer)
{
m_buffer = buffer;
}
bool read (MSWrite::Byte *buf, const MSWrite::DWord numBytes)
{
if (m_buffer->readBlock ((char *) buf, (TQ_ULONG) numBytes) != TQ_LONG (numBytes))
{
error (MSWrite::Error::FileError, "could not read from TQBuffer (not really a FileError)\n");
return false;
}
else
return true;
}
bool write (const MSWrite::Byte *buf, const MSWrite::DWord numBytes)
{
if (m_buffer->writeBlock ((char *) buf, (TQ_ULONG) numBytes) != TQ_LONG (numBytes))
{
error (MSWrite::Error::FileError, "could not write to TQBuffer (not really a FileError)\n");
return false;
}
else
return true;
}
// normally we must write zeros if we seek past EOF
// but we know that won't happen :)
bool seek (const long offset, const int whence)
{
long absoffset;
switch (whence)
{
case SEEK_SET:
absoffset = offset;
break;
case SEEK_CUR:
absoffset = m_buffer->at () + offset;
break;
case SEEK_END:
absoffset = m_buffer->size () + offset;
break;
default:
error (MSWrite::Error::InternalError, "unknown seek\n");
return false;
}
if (absoffset > long (m_buffer->size ()))
{
error (MSWrite::Error::InternalError, "seek past EOF unimplemented\n");
return false;
}
if (!m_buffer->at (absoffset))
{
error (MSWrite::Error::FileError, "TQBuffer could not seek (not really a FileError)\n");
return false;
}
return true;
}
long tell (void)
{
return long (m_buffer->at ());
}
void debug (const char *s)
{
kdDebug (30509) << s;
}
void debug (const int i)
{
kdDebug (30509) << i;
}
void error (const int errorCode, const char *message,
const char * /*file*/ = "", const int /*lineno*/ = 0,
MSWrite::DWord /*token*/ = MSWrite::Device::NoToken)
{
if (errorCode == MSWrite::Error::Warn)
kdWarning (30509) << message;
else
{
m_error = errorCode;
kdError (30509) << message;
}
}
};
class WMFRecord : public MSWrite::NeedsDevice
{
public:
static const int s_size = 6;
protected:
MSWrite::Byte m_data [s_size];
MSWrite::DWord m_size; // record size in Words including everything in this struct
MSWrite::Word m_function;
MSWrite::Short m_args [100]; // "ought to be enough for anybody"
int m_argUpto;
public:
WMFRecord () : m_argUpto (0)
{
}
WMFRecord (const MSWrite::DWord size, const MSWrite::Word function, MSWrite::Device *device)
: MSWrite::NeedsDevice (device),
m_size (size), m_function (function),
m_argUpto (0)
{
}
MSWrite::DWord getSize (void) const { return m_size; }
void setSize (const MSWrite::DWord size) { m_size = size; }
MSWrite::Word getFunction (void) const { return m_function; }
void setFunction (const MSWrite::Word function) { m_function = function; }
void add (const MSWrite::Short arg)
{
m_args [m_argUpto++] = arg;
}
bool readFromDevice (void)
{
if (!m_device->readInternal (m_data, 6)) return false;
MSWrite::ReadDWord (m_size, m_data + 0);
MSWrite::ReadWord (m_function, m_data + 4);
printf ("Size (Words): %i Size (Bytes): %i Function: %04X (func=%02X,numArgs=%i)\n",
m_size, m_size * sizeof (MSWrite::Word), m_function, m_function & 255, m_function >> 8);
#if 1
if (m_function == 0 && m_size == 3) // last record
return false;
#endif
switch (m_function)
{
case 0x0103:
printf ("\tSetMapMode\n");
break;
case 0x020c:
printf ("\tSetWindowExt\n");
break;
case 0x020b:
printf ("\tSetWindowOrg\n");
break;
case 0x0b41:
printf ("\tDibStretchBlt\n");
break;
default:
printf ("\tUnknown function\n");
}
long offset = m_device->tellInternal ();
for (int i = 0; i < ((m_function == 0x0b41) ? 10 : (m_function >> 8)); i++)
{
MSWrite::Byte data [2];
if (!m_device->readInternal (data, 2)) return false;
MSWrite::ReadShort (m_args [i], data);
printf ("\tArg (rev) #%i=%i\n", i, m_args [i]);
}
#if 0
// arguments are reversed normally
int u = 0;
for (int i = (m_function >> 8) - 1; i >= 0; i--, u++)
printf ("\tArg #%i=%u\n", u, m_args [i]);
#endif
#if 1
if (m_function == 0xb41)
{
// just curious but what's the infoHeader like?
MSWrite::BMP_BitmapInfoHeader bih;
bih.setDevice (m_device);
if (!bih.readFromDevice ()) return false;
}
#endif
m_size *= sizeof (MSWrite::Word); // in Bytes now
m_size -= 6; // skip past prefix
printf (">>> At: %li Next: %li\n", m_device->tellInternal (), offset + m_size);
if (!m_device->seekInternal (offset + m_size, SEEK_SET)) return false;
return true;
}
bool writeToDevice (void)
{
MSWrite::WriteDWord (m_size, m_data + 0);
MSWrite::WriteWord (m_function, m_data + 4);
if (!m_device->writeInternal (m_data, 6)) return false;
for (int i = 0; i < ((m_function == 0x0B41) ? 10/*not 11*/ : (m_function >> 8)); i++)
{
MSWrite::Byte data [2];
MSWrite::WriteShort (m_args [i], data);
if (!m_device->writeInternal (data, 2)) return false;
}
return true;
}
};
// converts a DIB to a Standard WMF
bool BMP2WMF (MSWrite::Device &readDevice, MSWrite::Device &writeDevice)
{
// read BMP's FileHeader
MSWrite::BMP_BitmapFileHeader bfh;
bfh.setDevice (&readDevice);
if (!bfh.readFromDevice ()) return false;
// WMF bitmap doesn't contain FileHeader
MSWrite::DWord totalBytes = bfh.getTotalBytes () - MSWrite::BMP_BitmapFileHeader::s_size;
// read BMP's InfoHeader to get some info...
MSWrite::BMP_BitmapInfoHeader bih;
bih.setDevice (&readDevice);
if (!bih.readFromDevice ()) return false;
// get some info about the image
MSWrite::Long width = bih.getWidth ();
MSWrite::Long height = bih.getHeight ();
// Note: not from LibMSWrite's wmf.cpp
kdDebug (30509) << "\t\tBIH: width(pt)=" << width
<< " height(pt)=" << height
<< " BPP=" << bih.getBitsPerPixel ()
<< endl;
kdDebug (30509) << "\t\tBIH: xPixelsPerMeter=" << bih.getXPixelsPerMeter ()
<< " yPixelsPerMeter=" << bih.getYPixelsPerMeter ()
<< endl;
// write WMF Header
MSWrite::WMFHeader wmfHeader;
wmfHeader.setDevice (&writeDevice);
MSWrite::DWord maxRecordSizeBytes
= (totalBytes
+ 10 * sizeof (MSWrite::Word)/*parameters for DibStretchBlt*/
+ WMFRecord::s_size);
wmfHeader.setFileSize ((MSWrite::WMFHeader::s_size
+ WMFRecord::s_size + 1 * sizeof (MSWrite::Word)/*SetMapMode*/
+ WMFRecord::s_size + 2 * sizeof (MSWrite::Word)/*SetWindowExt*/
+ WMFRecord::s_size + 2 * sizeof (MSWrite::Word)/*SetWindowOrg*/
+ maxRecordSizeBytes/*DibStretchBlt*/
+ WMFRecord::s_size/*Sentinel*/)
/ sizeof (MSWrite::Word));
wmfHeader.setMaxRecordSize (maxRecordSizeBytes / sizeof (MSWrite::Word));
if (!wmfHeader.writeToDevice ()) return false;
WMFRecord wmfRecordSetMapMode (4/*(Words)*/, 0x0103, &writeDevice);
wmfRecordSetMapMode.add (8/*MM_ANISOTROPIC*/);
if (!wmfRecordSetMapMode.writeToDevice ()) return false;
WMFRecord wmfRecordSetWindowExt (5/*(Words)*/, 0x020C, &writeDevice);
wmfRecordSetWindowExt.add (-height);
wmfRecordSetWindowExt.add (width);
if (!wmfRecordSetWindowExt.writeToDevice ()) return false;
WMFRecord wmfRecordSetWindowOrg (5/*(Words)*/, 0x020B, &writeDevice);
wmfRecordSetWindowOrg.add (0);
wmfRecordSetWindowOrg.add (0);
if (!wmfRecordSetWindowOrg.writeToDevice ()) return false;
WMFRecord wmfRecordBMP (maxRecordSizeBytes / sizeof (MSWrite::Word),
0x0B41/*DibStretchBlt*/,
&writeDevice);
wmfRecordBMP.add (32); // ?
wmfRecordBMP.add (204); // ?
wmfRecordBMP.add (height); // src height
wmfRecordBMP.add (width); // src width
wmfRecordBMP.add (0); // src y
wmfRecordBMP.add (0); // src x
wmfRecordBMP.add (-height); // dest height
wmfRecordBMP.add (width); // dest width
wmfRecordBMP.add (0); // dest y
wmfRecordBMP.add (0); // dest x
if (!wmfRecordBMP.writeToDevice ()) return false;
// write BMP InfoHeader back to the device
bih.setDevice (&writeDevice);
if (!bih.writeToDevice ()) return false;
long left = totalBytes - MSWrite::BMP_BitmapInfoHeader::s_size;
while (left)
{
MSWrite::Byte data [1024];
long amountToRead = left > 1024 ? 1024 : left;
if (!readDevice.readInternal (data, amountToRead)) return false;
if (!writeDevice.writeInternal (data, amountToRead)) return false;
left -= amountToRead;
}
WMFRecord wmfRecordSentinel (3/*(Words)*/, 0x0000, &writeDevice);
if (!wmfRecordSentinel.writeToDevice ()) return false;
#if 1
// bug with Word97?
MSWrite::Byte zero = 0;
if (!writeDevice.writeInternal (&zero, sizeof (MSWrite::Byte))) return false;
#endif
return true;
}
// all windows measurements depend on there being 72 dots/points per inch
static double getDimen72DPI (const int measurement, const int dotsPerMeter)
{
kdDebug (30509) << "\t\tgetDimen72DPI (measurement=" << measurement
<< ",dotsPerMeter=" << dotsPerMeter << ")" << endl;
// Can't get resolution?
// Assume that we are already 72dpi
if (dotsPerMeter <= 0)
return double (measurement);
// 2834.65 = 100 / 2.54 * 72
return double (measurement) * 2834.65 / double (dotsPerMeter);
}
//
// Note: if we suffer from a conversion error in this function (and can't
// export the image), we _still_ return true, not false because there is
// nothing worse than a filter that aborts due to its own incompetence [1]
// (don't flame the author please, just blame the function :)). But if we
// do suffer from a file-like error, we abort right away because something
// bad (memory corruption, internal error...) is happening!
//
// Why then _do_ we abort on text errors when images tell a thousand
// words? Because this saying is wrong and text is probably more
// important to the user.
//
// [1] yes, "worse than an itch you can't scratch"
//
bool processImage (const FrameAnchor &frameAnchor,
const MSWrite::FormatParaProperty *paraPropIn,
const MSWrite::FormatCharProperty *charPropIn,
const bool ignoreIndent)
{
kdDebug (30509) << "--------------------------" << endl
<< "processImage()" << endl;
// Write supports images in 3 formats:
//
// 1. Monochrome BMP (not very useful)
// 2. OLE (hard to work with and only supported in ver >= 3.1)
// 3. Standard WMF
//
// So we convert all images to WMF for convenience.
// We don't even bother saving Monochrome BMPs "as is" because
// there's no point (just save it in WMF to make life easier)
//
// But a Standard WMF is basically a BMP with some headers/GDI calls
// so the conversion process is like this:
//
// start->WMF->finish
// start->BMP->WMF->finish
// start->???->BMP->WMF->finish
//
double imageActualWidth = -1, imageActualHeight = -1;
MSWrite::DWord imageSize = 0;
TQString imageType;
int pos = frameAnchor.picture.koStoreName.findRev ('.');
if (pos != -1) imageType = frameAnchor.picture.koStoreName.mid (pos).lower ();
kdDebug (30509) << "\timageType: " << imageType << endl;
TQByteArray imageData;
kdDebug (30509) << "\tReading image: " << frameAnchor.picture.koStoreName << endl;
if (!loadSubFile (frameAnchor.picture.koStoreName, imageData))
ErrorAndQuit (MSWrite::Error::FileError, "could not open image from store\n");
// FSM
for (;;)
{
if (imageType == ".wmf")
{
imageSize = imageData.size ();
if (imageActualWidth == -1 && imageActualHeight == -1)
{
// load WMF
KoWmfPaint wmf;
if (!wmf.load (imageData))
{
kdError (30509) << "Could not open WMF - Invalid Format!" << endl;
return true;
}
// get raw dimensions
TQRect dimen = wmf.boundingRect ();
int width = abs (dimen.width ());
int height = abs (dimen.height ());
kdDebug (30509) << "\tRaw WMF dimensions: " << width << "x" << height << endl;
if (wmf.isPlaceable ())
{
kdDebug (30509) << "\tConverting Placeable WMF" << endl;
// convert twip measurements that aren't in 72dpi
int defaultDpi = wmf.defaultDpi ();
if (defaultDpi <= 0)
{
kdWarning (30509) << "Invalid defaultDPI: " << defaultDpi << endl;
defaultDpi = 1440;
}
imageActualWidth = width * 1440 / defaultDpi;
imageActualHeight = height * 1440 / defaultDpi;
// Remove Aldus Placeable WMF Header
for (int i = 0; i < int (imageSize) - 22; i++)
imageData [i] = imageData [i + 22];
imageData.resize (imageSize - 22);
imageSize -= 22;
}
else if (wmf.isEnhanced ())
{
kdError (30509) << "Enhanced WMF unsupported by TQWmf, internal error!" << endl;
return true;
}
// Standard WMF
else
{
kdDebug (30509) << "\tStandard WMF - no conversion required" << endl;
// assume width & height were in 72dpi points
imageActualWidth = Point2Twip (width);
imageActualHeight = Point2Twip (height);
}
}
kdDebug (30509) << "\tNow WMF: width=" << imageActualWidth
<< " height=" << imageActualHeight
<< " size=" << imageSize
<< endl;
// we're done!
break;
}
// TODO: DDB?
else if (imageType == ".bmp")
{
TQImage image (imageData);
if (image.isNull ())
{
kdError (30509) << "TQImage IsNull: Line=" << __LINE__ << endl;
return true;
}
if (imageActualWidth == -1 && imageActualHeight == -1)
{
imageActualWidth = Point2Twip (getDimen72DPI (image.width (), image.dotsPerMeterX ()));
imageActualHeight = Point2Twip (getDimen72DPI (image.height (), image.dotsPerMeterY ()));
}
kdDebug (30509) << "\tNow BMP: width=" << imageActualWidth
<< " height=" << imageActualHeight
<< " size=" << imageSize
<< endl;
TQByteArray imageWMF;
// input device
TQBuffer inBuffer (imageData);
inBuffer.open (IO_ReadOnly);
TQBufferDevice inDevice (&inBuffer);
// output device
TQBuffer outBuffer (imageWMF);
outBuffer.open (IO_WriteOnly);
TQBufferDevice outDevice (&outBuffer);
// BMP --> WMF
if (!BMP2WMF (inDevice, outDevice))
{
kdError (30509) << "BMP to WMF conversion error" << endl;
return true;
}
outBuffer.close ();
inBuffer.close ();
imageData = imageWMF.copy ();
imageType = ".wmf";
}
else
{
if (imageActualWidth == -1 && imageActualHeight == -1)
{
TQImage image (imageData);
if (image.isNull())
{
kdError (30509) << "TQImage isNull: Line=" << __LINE__ << endl;
return true;
}
imageActualWidth = Point2Twip (getDimen72DPI (image.width (), image.dotsPerMeterX ()));
imageActualHeight = Point2Twip (getDimen72DPI (image.height (), image.dotsPerMeterY ()));
}
kdDebug (30509) << "\tForeign format: width=" << imageActualWidth
<< " height=" << imageActualHeight
<< " size=" << imageSize
<< endl;
TQByteArray imageBMP;
// input device
TQBuffer inBuffer (imageData);
inBuffer.open (IO_ReadOnly);
// read foreign image
TQImageIO imageIO (&inBuffer, NULL);
if (!imageIO.read ())
{
kdError (30509) << "Could not read foreign format" << endl;
return true;
}
// output device
TQBuffer outBuffer (imageBMP);
outBuffer.open (IO_WriteOnly);
// write BMP
imageIO.setIODevice (TQT_TQIODEVICE(&outBuffer));
imageIO.setFormat ("BMP");
if (!imageIO.write ())
{
kdError (30509) << "Could not convert to BMP" << endl;
return true;
}
outBuffer.close ();
inBuffer.close ();
imageData = imageBMP.copy ();
imageType = ".bmp";
}
}
kdDebug (30509) << "\tActual dimensions: width=" << imageActualWidth
<< " height=" << imageActualHeight << endl;
kdDebug (30509) << "\tKOffice position: left=" << frameAnchor.frame.left
<< " right=" << frameAnchor.frame.right
<< " top=" << frameAnchor.frame.top
<< " bottom=" << frameAnchor.frame.bottom
<< endl;
kdDebug (30509) << "\tIndent=" << MSWrite::Word (Point2Twip (frameAnchor.frame.left)) - m_leftMargin << endl;
if (ignoreIndent)
kdDebug (30509) << "\t\tIgnoring indent - already exported at least one image in a KWord paragraph" << endl;
double displayedWidth = Point2Twip (frameAnchor.frame.right - frameAnchor.frame.left + 1);
double displayedHeight = Point2Twip (frameAnchor.frame.bottom - frameAnchor.frame.top + 1);
kdDebug (30509) << "\tdisplayedWidth=" << displayedWidth
<< " displayedHeight=" << displayedHeight
<< endl;
//
// Start writing out the image now
//
// Note: here, we can start returning false again because the errors
// won't be conversion-related
//
MSWrite::Image image;
image.setIsWMF (true);
if (!ignoreIndent)
{
if (paraPropIn->getAlignment () != MSWrite::Alignment::Centre)
image.setIndent (MSWrite::Word (Point2Twip (frameAnchor.frame.left)) - m_leftMargin);
else
{
// TODO: what is the image offset relative to (it's not always rel. to the left margin)?
kdDebug (30509) << "\tCentred paragraph, cannot position image" << endl;
}
}
image.setOriginalWidth (imageActualWidth);
image.setOriginalHeight (imageActualHeight);
image.setDisplayedWidth (displayedWidth);
image.setDisplayedHeight (displayedHeight);
image.setExternalImageSize (imageSize);
MSWrite::FormatParaProperty paraProp; paraProp = *paraPropIn;
paraProp.setIsObject (true);
paraProp.setLeftIndent (0); // not necessary but...
if (!m_generator->writeParaInfoBegin (&paraProp, NULL, &image))
return false;
// yes, images have character formatting as well
// (character formatting _must_ cover entire document)
// (but I think it's ignored)
MSWrite::FormatCharProperty charProp; charProp = *charPropIn;
if (!m_generator->writeCharInfoBegin (&charProp))
return false;
// actually write image
if (!m_generator->writeBinary ((const MSWrite::Byte *) (const char *) imageData.data (), imageSize)) return false;
// 2nd argument endOfParagraph is ignored by InternalGenerator so
// there's no need to specify it
if (!m_generator->writeCharInfoEnd (&charProp, true))
return false;
;
if (!m_generator->writeParaInfoEnd (&paraProp, NULL, &image))
return false;
kdDebug (30509) << "processImage() successful!" << endl
<< "==========================" << endl
<< endl
<< endl;
return true;
}
bool processTable (const Table &table)
{
// just dump the table out for now (no layout)
for (TQValueList <TableCell>::ConstIterator it = table.cellList.begin ();
it != table.cellList.end ();
it++)
{
if (!doFullParagraphList (*(*it).paraList)) return false;
}
return true;
}
bool processCounter (const CounterData &counter)
{
//kdDebug (30509) << "processCounter(counter.text=" << counter.text << ")" << endl;
if (!counter.text.isEmpty ())
{
// isn't this wonderful? :)
if (!processText (counter.text)) return false;
if (!processText (" ")) return false;
}
return true;
}
#ifndef NDEBUG
//#define KMF_DEBUG_FONT
#endif
void processFormatData (MSWrite::FormatCharProperty &charProp,
const TextFormatting &f)
{
if (!f.fontName.isEmpty ())
{
// create new Font with Name
MSWrite::Font font ((const MSWrite::Byte *) (const char *) f.fontName.utf8 ());
#ifdef KMF_DEBUG_FONT
kdDebug (30509) << "FontName " << f.fontName << endl;
#endif
// get Font Family
TQFont TQTFontInfo (f.fontName);
switch (TQTFontInfo.styleHint ())
{
case TQFont::Serif:
#ifdef KMF_DEBUG_FONT
kdDebug (30509) << "FontFamily Serif" << endl;
#endif
font.setFamily (MSWrite::Font::Roman);
break;
case TQFont::SansSerif:
#ifdef KMF_DEBUG_FONT
kdDebug (30509) << "FontFamily SansSerif" << endl;
#endif
font.setFamily (MSWrite::Font::Swiss);
break;
case TQFont::Courier:
#ifdef KMF_DEBUG_FONT
kdDebug (30509) << "FontFamily Courier" << endl;
#endif
font.setFamily (MSWrite::Font::Modern);
break;
case TQFont::OldEnglish:
#ifdef KMF_DEBUG_FONT
kdDebug (30509) << "FontFamily OldEnglish" << endl;
#endif
font.setFamily (MSWrite::Font::Decorative);
break;
default:
#ifdef KMF_DEBUG_FONT
kdDebug (30509) << "FontFamily DontKnow" << endl;
#endif
// it's either DontCare or MSWrite::Font::Script
font.setFamily (MSWrite::Font::DontCare);
break;
}
charProp.setFont (&font);
}
if (f.fontSize > 0) charProp.setFontSize (f.fontSize);
charProp.setIsItalic (f.italic);
charProp.setIsUnderlined (f.underline); // TODO: underlineWord
charProp.setIsBold (f.weight > (50/*normal*/ + 75/*bold*/) / 2);
switch (f.verticalAlignment)
{
case 0: // normal
charProp.setIsNormalPosition ();
break;
case 1: // subscript
charProp.setIsSubscript ();
break;
case 2: // superscript
charProp.setIsSuperscript ();
break;
}
// TODO: fontAttribute;
}
static MSWrite::Word getClosestLineSpacing (const double points)
{
const double twips = Point2Twip (points);
#if 1
if (twips < double ((MSWrite::LineSpacing::Single + MSWrite::LineSpacing::OneAndAHalf) / 2))
return MSWrite::LineSpacing::Single;
else if (twips < double ((MSWrite::LineSpacing::OneAndAHalf + MSWrite::LineSpacing::Double) / 2))
return MSWrite::LineSpacing::OneAndAHalf;
else
return MSWrite::LineSpacing::Double;
#else // or do we want a non-"standard" linespacing?
return MSWrite::Word (twips);
#endif
}
bool doFullParagraphList (const TQValueList <ParaData> &paraList)
{
for (TQValueList <ParaData>::ConstIterator it = paraList.begin ();
it != paraList.end ();
it ++)
{
if (!doFullParagraph (*it)) return false;
}
return true;
}
bool doFullParagraph (const ParaData &paraData)
{
return doFullParagraph (paraData.text,
paraData.layout,
paraData.formattingList);
}
bool doFullParagraph (const TQString &paraText,
const LayoutData &layout,
const ValueListFormatData &paraFormatDataList)
{
MSWrite::FormatParaProperty paraProp;
if (m_inWhat == Body)
paraProp.setIsNormalParagraph (true);
else
{
if (m_inWhat == Header)
{
paraProp.setIsHeader (true);
paraProp.setIsOnFirstPage (m_isHeaderOnFirstPage);
}
else if (m_inWhat == Footer)
{
paraProp.setIsFooter (true);
paraProp.setIsOnFirstPage (m_isFooterOnFirstPage);
}
}
paraProp.setIsText (true);
// Alignment
if (!layout.alignment.isEmpty ())
{
if (layout.alignment == "left")
// quite useless since MSWrite::Alignment::Left is the default anyway
paraProp.setAlignment (MSWrite::Alignment::Left);
else if (layout.alignment == "right")
paraProp.setAlignment (MSWrite::Alignment::Right);
else if (layout.alignment == "center")
paraProp.setAlignment (MSWrite::Alignment::Center);
else if (layout.alignment == "justify")
paraProp.setAlignment (MSWrite::Alignment::Justify);
else
kdWarning (30509) << "Unknown Alignment: " << layout.alignment << endl;
}
// Indentation
if (layout.indentFirst) paraProp.setLeftIndentFirstLine (MSWrite::Short (Point2Twip (layout.indentFirst)));
if (layout.indentLeft >= 0) paraProp.setLeftIndent (MSWrite::Word (Point2Twip (layout.indentLeft)));
if (layout.indentRight >= 0) paraProp.setRightIndent (MSWrite::Word (Point2Twip (layout.indentRight)));
#if 0
kdDebug (30509) << "Indent: " << Point2Twip (layout.indentFirst) << " "
<< Point2Twip (layout.indentLeft) << " "
<< Point2Twip (layout.indentRight) << endl;
#endif
// Line Spacing
MSWrite::Word lineSpacing = MSWrite::LineSpacing::Normal;
switch (layout.lineSpacingType)
{
case LayoutData::LS_SINGLE:
lineSpacing = MSWrite::LineSpacing::Normal;
break;
case LayoutData::LS_ONEANDHALF:
lineSpacing = MSWrite::LineSpacing::OneAndAHalf;
break;
case LayoutData::LS_DOUBLE:
lineSpacing = MSWrite::LineSpacing::Double;
break;
case LayoutData::LS_CUSTOM:
case LayoutData::LS_FIXED:
case LayoutData::LS_ATLEAST:
lineSpacing = getClosestLineSpacing (layout.lineSpacing);
break;
case LayoutData::LS_MULTIPLE:
break;
default:
kdWarning (30509) << "unknown lineSpacingType \'" << layout.lineSpacingType << "\'" << endl;
}
paraProp.setLineSpacing (lineSpacing);
// Tabs are a Document Property, not a Paragraph Property, in Write, yet are stored for each paragraph.
// It seems that Write applies the 1st paragraph's Tabulator settings to the _entire_ document
// Word97 and KWord, however, will treat them like a Paragraph Property
int numTabs = 0;
for (TabulatorList::ConstIterator tabIt = layout.tabulatorList.begin ();
tabIt != layout.tabulatorList.end ();
tabIt++)
{
MSWrite::FormatParaPropertyTabulator tab;
// Write's UI only supports 12 as opposed to the 14 supposedly
// supported in the file so let's play it safe and quit when
// we reach 12
// Actually, KWord's UI also only supports 12 so this should never be true
if (numTabs >= 12)
{
kdWarning (30509) << "Write does not support more 12 tabulators, not writing out all tabulators" << endl;
break;
}
// Write only supports Decimal and Left tabs
// TODO: KOffice 1.3 alignchar (modify libexport)
if ((*tabIt).m_type == 3 /* && (*tabIt).m_alignchar == '.' */)
tab.setIsDecimal ();
else
tab.setIsNormal ();
tab.setIndent (MSWrite::Word (Point2Twip ((*tabIt).m_ptpos)));
// int m_filling;
// double m_width;
if ((*tabIt).m_filling != TabulatorData::TF_NONE)
kdWarning (30509) << "Write does not support Tabulator Filling" << endl;
paraProp.addTabulator (&tab);
numTabs++;
}
// TODO: double marginTop; // space before the paragraph (a negative value means invalid)
// TODO: double marginBottom; // space after the paragraph (a negative value means invalid)
// TODO: TQString styleName;
// TODO: TQString styleFollowing;
if (!m_generator->writeParaInfoBegin (&paraProp)) return false;
// get this paragraph's "default formatting"
MSWrite::FormatCharProperty charPropDefault;
processFormatData (charPropDefault, layout.formatData.text);
MSWrite::DWord uptoByte = 0; // relative to start of KWord paragraph
MSWrite::DWord numBytes = paraText.length (); // relative to start of KWord paragraph
bool startOfWRIParagraph = true;
bool exportedAtLeastOneImage = false; // ...from the KWord paragraph
// empty paragraph
if (numBytes == 0)
{
//kdDebug (30509) << "Outputting empty paragraph!" << endl;
// write default character property start
if (!m_generator->writeCharInfoBegin (&charPropDefault)) return false;
// page break at start of paragraph?
if (layout.pageBreakBefore)
if (!m_generator->writePageBreak ()) return false;
// counter data
processCounter (layout.counter);
// end of line
if (!m_generator->writeCarriageReturn ()) return false;
if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
// page break at end of paragraph?
if (layout.pageBreakAfter)
if (!m_generator->writePageBreak ()) return false;
// write default character property end
if (!m_generator->writeCharInfoEnd (&charPropDefault, true)) return false;
}
else
{
for (ValueListFormatData::ConstIterator formatIt = paraFormatDataList.begin ();
formatIt != paraFormatDataList.end ();
formatIt++)
{
bool textSegment = true;
// apply local <FORMAT> tag on top of "default formatting"
MSWrite::FormatCharProperty charProp; charProp = charPropDefault;
processFormatData (charProp, (*formatIt).text);
if (!m_generator->writeCharInfoBegin (&charProp)) return false;
if (uptoByte == 0)
{
// page break at start of paragraph?
if (layout.pageBreakBefore)
if (!m_generator->writePageBreak ()) return false;
// counter data
processCounter (layout.counter);
}
// yes, this is slightly premature but it doesn't matter
// ... just be careful when using uptoByte
uptoByte += (*formatIt).len;
switch ((*formatIt).id)
{
case 0: // none?
ErrorAndQuit (MSWrite::Error::InternalError, "Format ID = 0\n");
case 1: // text
if (!processText (paraText.mid ((*formatIt).pos, (*formatIt).len)))
/*uptoByte == numBytes))*/
return false;
startOfWRIParagraph = false;
break;
case 2: // picture (deprecated)
m_device->error (MSWrite::Error::Warn, "Picture (deprecated) unsupported\n");
break;
case 3: // tabulator (deprecated)
m_device->error (MSWrite::Error::Warn, "Tabulator (deprecated) unsupported\n");
break;
case 4: // variable
{
bool justPrintText = true;
// Page Number / Number of Pages
if ((*formatIt).variable.m_type == 4 &&
(*formatIt).variable.isPageNumber () &&
m_inWhat != Body/*Write replaces it with '*' in the body*/)
{
if (!m_generator->writeCharInfoEnd (&charProp)) return false;
charProp.setIsPageNumber (true);
// if you don't do this it will print out the character literally (char 1)
if (!m_generator->writeCharInfoBegin (&charProp)) return false;
// only variable Write supports (and only in headers/footers)
// if you don't write out char 1, Write will not treat it as a
// variable anchor and will print the character out literally
if (!m_generator->writePageNumber ()) return false;
if (!m_generator->writeCharInfoEnd (&charProp)) return false;
charProp.setIsPageNumber (false);
if (!m_generator->writeCharInfoBegin (&charProp)) return false;
justPrintText = false;
}
if (justPrintText)
{
if (!processText ((*formatIt).variable.m_text))
return false;
}
startOfWRIParagraph = false;
break;
}
case 5: // footnote (KOffice 1.1)
m_device->error (MSWrite::Error::Warn, "Footnote unsupported\n");
break;
case 6: // anchor for frame
//
// Write does not support inline frames so:
//
// - we end the current paragraph
// - dump the inline frame in a paragraph of its own
// - continue with a new paragraph
//
if (!startOfWRIParagraph)
{
kdDebug (30509) << "Writing CRLF to end text paragraph" << endl;
// If you don't have CRLF at the end of the text
// paragraph, Write will think that the next paragraph
// (the image) is part of the text...
if (!m_generator->writeCarriageReturn ()) return false;
if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
}
else
kdDebug (30509) << "Inline frame is anchored at start of paragraph, no CRLF" << endl;
if (!m_generator->writeCharInfoEnd (&charProp)) return false;
if (!m_generator->writeParaInfoEnd (&paraProp)) return false;
if ((*formatIt).frameAnchor.type == 6)
{
kdDebug (30509) << "Table detected" << endl;
// this will make its own paragraph(s)...
processTable ((*formatIt).frameAnchor.table);
// HACK: inline tables are flushed to the left and right
// margins, despite being inline, hence the next image
// indent will be sensible and should not be ignored.
kdDebug (30509) << "Table hack: resetting image-ignore-indent flag" << endl;
exportedAtLeastOneImage = false;
}
else if ((*formatIt).frameAnchor.type == 2)
{
kdDebug (30509) << "Image detected" << endl;
// this will make its own paragraph...
if (!processImage ((*formatIt).frameAnchor, &paraProp, &charProp,
exportedAtLeastOneImage)) return false;
exportedAtLeastOneImage = true;
}
else
kdWarning (30509) << "Unknown type of anchor: " << (*formatIt).frameAnchor.type << endl;
// recontinue paragraph
if (!m_generator->writeParaInfoBegin (&paraProp)) return false;
startOfWRIParagraph = true;
if (!m_generator->writeCharInfoBegin (&charProp)) return false;
textSegment = false;
break;
}
if (uptoByte == numBytes)
{
if (textSegment)
{
// end of line
if (!m_generator->writeCarriageReturn ()) return false;
if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
}
// page break at end of paragraph?
if (layout.pageBreakAfter)
if (!m_generator->writePageBreak ()) return false;
}
if (!m_generator->writeCharInfoEnd (&charProp, uptoByte == numBytes)) return false;
}
}
if (!m_generator->writeParaInfoEnd (&paraProp)) return false;
//if (numBytes) kdDebug (30509) << "Just Output " << uptoByte << "/" << numBytes << " with text \'" << paraText.utf8 () << "\'" << endl;
return true;
}
template <class dtype>
dtype min (const dtype a, const dtype b, const dtype c)
{
if (a <= b && a <= c) return a;
if (b <= a && b <= c) return b;
return c;
}
#ifndef NDEBUG
//#define DEBUG_PROCESS_TEXT
#endif
bool processText (const TQString &stringUnicode)
{
//
// Look out for characters in the string and emit signals as appropriate:
//
// 1 pageNumber (already taken care of as a variable)
// 10 newLine
// 13 carriageReturn (TODO: Oh no! Can't happen!)
// 12 pageBreak (TODO: we are in real trouble: this can actually happen without forcing a new paragraph!)
// 31 optionalHyphen
// ? text
//
int softHyphen = -2, nonBreakingSpace = -2, newLine = -2;
int upto = 0;
int stringUnicodeLength = stringUnicode.length ();
while (upto < stringUnicodeLength)
{
//
// look for KWord's special characters as defined in the DTD
//
if (softHyphen == -2)
{
softHyphen = stringUnicode.find (TQChar (0xAD), upto);
if (softHyphen == -1) softHyphen = INT_MAX;
}
if (nonBreakingSpace == -2)
{
nonBreakingSpace = stringUnicode.find (TQChar (0xA0), upto);
if (nonBreakingSpace == -1) nonBreakingSpace = INT_MAX;
}
if (newLine == -2)
{
newLine = stringUnicode.find (TQChar ('\n'), upto);
if (newLine == -1) newLine = INT_MAX;
}
// look for the closest one
int specialLocation = min (softHyphen, nonBreakingSpace, newLine);
// get substring (either to the end of the original string or before
// the next closest special character, if any)
int length = stringUnicodeLength - upto;
if (specialLocation != INT_MAX)
length = specialLocation - upto;
TQString substring = stringUnicode.mid (upto, length);
#ifdef DEBUG_PROCESS_TEXT
kdDebug (30509) << "Parent string: upto=" << upto
<< ",length=" << stringUnicode.length () << endl;
kdDebug (30509) << "Child string: length=" << length
<< " (specialLoc=" << specialLocation << ")" << endl;
#endif
//
// convert substring to windows-1251
//
TQCString stringWin;
// there is a codec, therefore there is an encoder...
if (m_codec)
{
int len; // don't overwrite length, we need it later
// convert from Unicode (UTF8)
stringWin = m_encoder->fromUnicode (substring, len = length);
}
else
{
// output a plain string still in wrong Character Set
// (hopefully the user won't notice)
stringWin = substring.utf8 ();
}
// output encoded text
if (!m_generator->writeText ((const MSWrite::Byte *) (const char *) stringWin))
return false;
upto += length;
// special character?
if (specialLocation != INT_MAX)
{
#ifdef DEBUG_PROCESS_TEXT
kdDebug (30509) << "Found special character!" << endl;
#endif
// output special character
if (specialLocation == softHyphen)
{
#ifdef DEBUG_PROCESS_TEXT
kdDebug (30509) << "\tSoft Hyphen" << endl;
#endif
if (!m_generator->writeOptionalHyphen ()) return false;
softHyphen = -2;
}
else if (specialLocation == nonBreakingSpace)
{
#ifdef DEBUG_PROCESS_TEXT
kdDebug (30509) << "\tNon-breaking Space" << endl;
#endif
// don't think Write supports nonBreakingSpace
if (!m_generator->writeText ((const MSWrite::Byte *) " ")) return false;
nonBreakingSpace = -2;
}
else if (specialLocation == newLine)
{
#ifdef DEBUG_PROCESS_TEXT
kdDebug (30509) << "\tNew Line" << endl;
#endif
// \r\n, not just \n
if (!m_generator->writeCarriageReturn ()) return false;
if (!m_generator->writeNewLine (true/*InternalGenerator doesn't care*/)) return false;
newLine = -2;
}
else
{
ErrorAndQuit (MSWrite::Error::InternalError, "simply impossible specialLocation\n");
}
// skip past special character
upto++;
}
} // while (upto < stringUnicodeLength) {
return true;
}
};
MSWriteExport::MSWriteExport (KoFilter *, const char *, const TQStringList &)
: KoFilter()
{
}
MSWriteExport::~MSWriteExport ()
{
}
KoFilter::ConversionStatus MSWriteExport::convert (const TQCString &from, const TQCString &to)
{
kdDebug (30509) << "MSWriteExport $Date: 2006-02-12 19:28:12 +0100 (Sun, 12 Feb 2006) $ using LibMSWrite "
<< MSWrite::Version << endl;
if (to != "application/x-mswrite" || from != "application/x-kword")
{
kdError (30509) << "Internal error! Filter not implemented?" << endl;
return KoFilter::NotImplemented;
}
KWordMSWriteWorker *worker = new KWordMSWriteWorker;
if (!worker)
{
kdError (30509) << "Could not allocate memory for worker" << endl;
return KoFilter::OutOfMemory;
}
KWEFKWordLeader *leader = new KWEFKWordLeader (worker);
if (!leader)
{
kdError (30509) << "Could not allocate memory for leader" << endl;
delete worker;
return KoFilter::OutOfMemory;
}
KoFilter::ConversionStatus ret = leader->convert (m_chain, from, to);
int errorCode = worker->getError ();
delete leader;
delete worker;
// try to return somewhat more meaningful errors than KoFilter::StupidError
// for the day that KOffice actually reports them to the user properly
switch (errorCode)
{
case MSWrite::Error::Ok:
kdDebug (30509) << "Returning error code " << ret << endl;
return ret; // not KoFilter::OK in case KWEFKWordLeader wants to report something
case MSWrite::Error::Warn:
kdDebug (30509) << "Error::Warn" << endl;
return KoFilter::InternalError; // warnings should _never_ set m_error
case MSWrite::Error::InvalidFormat:
kdDebug (30509) << "Error::InvalidFormat" << endl;
return KoFilter::InternalError; // how can the file I'm _writing_ be of an invalid format?
case MSWrite::Error::OutOfMemory:
kdDebug (30509) << "Error::OutOfMemory" << endl;
return KoFilter::OutOfMemory;
case MSWrite::Error::InternalError:
kdDebug (30509) << "Error::InternalError" << endl;
return KoFilter::InternalError;
case MSWrite::Error::Unsupported:
kdDebug (30509) << "Error::Unsupported" << endl;
return KoFilter::InternalError;
case MSWrite::Error::FileError:
kdDebug (30509) << "Error::FileError" << endl;
return KoFilter::CreationError;
}
kdWarning (30509) << "Unknown error" << endl;
return KoFilter::StupidError;
}
#include <mswriteexport.moc>