/* AbiSource Program Utilities * Copyright (C) 2002 Dom Lachowicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include #include #include "ie_impexp_OpenWriter.h" #include "ie_exp_OpenWriter.h" #include "ut_debugmsg.h" /*****************************************************************************/ /*****************************************************************************/ IE_Exp_OpenWriter_Sniffer::IE_Exp_OpenWriter_Sniffer() : IE_ExpSniffer ("OpenWriter::SXW") { } IE_Exp_OpenWriter_Sniffer::~IE_Exp_OpenWriter_Sniffer() { } /*! * Recognize this suffix */ bool IE_Exp_OpenWriter_Sniffer::recognizeSuffix(const char * szSuffix) { return (!UT_stricmp(szSuffix,".sxw")); } /*! * Construct an importer for us */ UT_Error IE_Exp_OpenWriter_Sniffer::constructExporter(PD_Document * pDocument, IE_Exp ** ppie) { *ppie = new IE_Exp_OpenWriter (pDocument); return UT_OK; } /*! * Get the dialog labels */ bool IE_Exp_OpenWriter_Sniffer::getDlgLabels(const char ** pszDesc, const char ** pszSuffixList, IEFileType * ft) { *pszDesc = "OpenOffice Writer (.sxw)"; *pszSuffixList = "*.sxw"; *ft = getFileType(); return true; } /*****************************************************************************/ /*****************************************************************************/ /*! * Write out a message to the stream. Message is an array of content */ static void writeToStream (GsfOutput * stream, const char * const message [], size_t nElements) { for(UT_uint32 k = 0; k < nElements; k++) gsf_output_write(stream, strlen(message[k]), reinterpret_cast(message[k])); } static void writeString (GsfOutput * output, const UT_String & str) { gsf_output_write (output, str.length(), reinterpret_cast(str.c_str())); } static void writeUTF8String (GsfOutput * output, const UT_UTF8String & str) { gsf_output_write (output, str.byteLength(), reinterpret_cast(str.utf8_str())); } static void outputCharData (GsfOutput * output, const UT_UCSChar * data, UT_uint32 length) { UT_UTF8String sBuf; const UT_UCSChar * pData; UT_ASSERT(sizeof(UT_Byte) == sizeof(char)); for (pData=data; (pData': sBuf += ">"; pData++; break; case '&': sBuf += "&"; pData++; break; case UCS_TAB: sBuf += "\t"; pData++; break; default: if (*pData < 0x20) // Silently eat these characters. pData++; else { sBuf.appendUCS4 (pData, 1); pData++; } } } writeUTF8String (output, sBuf); } /*****************************************************************************/ /*****************************************************************************/ OO_WriterImpl::OO_WriterImpl(GsfOutfile *pOutfile, OO_StylesContainer *pStylesContainer) : OO_ListenerImpl(), m_pStylesContainer(pStylesContainer) { m_pContentStream = gsf_outfile_new_child (pOutfile, "content.xml", FALSE); static const char * const preamble [] = { "\n", "\n", "\n", "\n" "\n", }; writeToStream(m_pContentStream, preamble, NrElements(preamble)); UT_Vector *tempStylesValuesList = m_pStylesContainer->enumerateSpanStyles(); UT_Vector *tempStylesKeysList = m_pStylesContainer->getSpanStylesKeys(); for (UT_uint32 i=0; isize(); i++) { int *styleNum = static_cast(tempStylesValuesList->getNthItem(i)); UT_String *styleProps = static_cast(tempStylesKeysList->getNthItem(i)); UT_String styleString = UT_String_sprintf("\n", *styleNum, "text", styleProps->c_str()); writeString(m_pContentStream, styleString); UT_DEBUGMSG(("%s", styleString.c_str())); UT_DEBUGMSG(("\"%s\"\n", styleProps->c_str())); } delete(tempStylesKeysList); delete(tempStylesValuesList); //m_acc.writeStylePreamble(m_contentStream); static const char * const midsection [] = { "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" }; writeToStream(m_pContentStream, midsection, NrElements(midsection)); } OO_WriterImpl::~OO_WriterImpl() { static const char * const postamble [] = { "\n", "\n" }; writeToStream(m_pContentStream, postamble, NrElements(postamble)); gsf_output_close(m_pContentStream); g_object_unref(G_OBJECT(m_pContentStream)); } void OO_WriterImpl::insertText(const UT_UCSChar * data, UT_uint32 length) { outputCharData(m_pContentStream, data, length); } void OO_WriterImpl::openBlock() { UT_UTF8String begP (""); writeUTF8String (m_pContentStream, begP); } void OO_WriterImpl::closeBlock() { UT_UTF8String endP ("\n"); writeUTF8String (m_pContentStream, endP); } void OO_WriterImpl::openSpan(UT_String &props) { UT_UTF8String spanString = UT_UTF8String_sprintf("", m_pStylesContainer->getStyleNum(props)); writeUTF8String(m_pContentStream, spanString); } void OO_WriterImpl::closeSpan() { UT_UTF8String closeSpan = ""; writeUTF8String(m_pContentStream, closeSpan); } void OO_StylesContainer::addSpanStyle(UT_String &key) { const void *temp; //if (!m_spanStylesHash.contains(key.utf8_str(), temp)) if (!m_spanStylesHash.pick(key.c_str())) { UT_DEBUGMSG(("OO_AccumulatorImpl: props of this type: %s not yet found, adding style at num: %i\n", key.c_str(), (m_spanStylesHash.size()+1))); int *val = new int; char *keyCopy = new char[strlen(key.c_str())+1]; keyCopy = strcpy(keyCopy, key.c_str()); *val = (int)m_spanStylesHash.size()+1; m_spanStylesHash.insert(keyCopy, val); } else { UT_DEBUGMSG(("OO_AccumulatorImpl: props of this type: %s already there, forget it\n", key.c_str())); } } const int OO_StylesContainer::getStyleNum(UT_String &key) const { if (int *val = reinterpret_cast(const_cast(m_spanStylesHash.pick(key.c_str())))) { return *val; } else return 0; } UT_Vector * OO_StylesContainer::enumerateSpanStyles() const { return m_spanStylesHash.enumerate(); } UT_Vector * OO_StylesContainer::getSpanStylesKeys() const { return m_spanStylesHash.keys(); } void OO_AccumulatorImpl::openSpan(UT_String &props) { m_pStylesContainer->addSpanStyle(props); } OO_Listener::OO_Listener (PD_Document * pDocument, IE_Exp_OpenWriter * pie, OO_ListenerImpl *pListenerImpl) : PL_Listener (), m_pDocument(pDocument), m_pie(pie), m_pListenerImpl(pListenerImpl), m_bInBlock(false), m_bInSpan(false) { } bool OO_Listener::populate(PL_StruxFmtHandle sfh, const PX_ChangeRecord * pcr) { switch (pcr->getType()) { case PX_ChangeRecord::PXT_InsertSpan: { const PX_ChangeRecord_Span * pcrs = static_cast(pcr); PT_BufIndex bi = pcrs->getBufIndex(); PT_AttrPropIndex api = pcr->getIndexAP(); if (api) { _openSpan(api); } m_pListenerImpl->insertText(m_pDocument->getPointer(bi), pcrs->getLength()); if (api) { _closeSpan(); } return true; } default: break; } } bool OO_Listener::populateStrux(PL_StruxDocHandle sdh, const PX_ChangeRecord * pcr, PL_StruxFmtHandle * psfh) { const PX_ChangeRecord_Strux * pcrx = static_cast (pcr); *psfh = 0; // we don't need it. switch (pcrx->getStruxType()) { case PTX_Block: { _closeSpan (); _openBlock(pcr->getIndexAP()); } } return true; } bool OO_Listener::change(PL_StruxFmtHandle sfh, const PX_ChangeRecord * pcr) { UT_ASSERT_NOT_REACHED(); return true; } bool OO_Listener::insertStrux(PL_StruxFmtHandle sfh, const PX_ChangeRecord * pcr, PL_StruxDocHandle sdh, PL_ListenerId lid, void (* pfnBindHandles)(PL_StruxDocHandle sdhNew, PL_ListenerId lid, PL_StruxFmtHandle sfhNew)) { UT_ASSERT_NOT_REACHED(); return true; } bool OO_Listener::signal(UT_uint32 iSignal) { UT_ASSERT_NOT_REACHED(); return true; } void OO_Listener::endDocument() { _closeBlock(); } void OO_Listener::_openBlock (PT_AttrPropIndex apiSpan) { if (m_bInBlock) _closeBlock(); m_pListenerImpl->openBlock(); m_bInBlock = true; } void OO_Listener::_closeBlock () { if (!m_bInBlock) return; m_pListenerImpl->closeBlock(); m_bInBlock = false; } // define some attribute bits #define IMP_SUPERSCRIPT_BIT 0 #define IMP_SUBSCRIPT_BIT 2 #define IMP_ITALICS_BIT 4 #define IMP_BOLD_BIT 8 #define IMP_STRIKEOUT_BIT 16 #define IMP_UNDERLINE_BIT 32 void OO_Listener::_openSpan(PT_AttrPropIndex api) { if (!m_bInBlock) { return; } const PP_AttrProp * pAP = NULL; bool bHaveProp = m_pDocument->getAttrProp(api,&pAP); UT_String props; if (bHaveProp && pAP) { const XML_Char * szValue; if ((pAP->getProperty("text-position", szValue)) && !UT_strcmp(szValue, "superscript")) { props += "style:text-position=\"super 58%\" "; } if ((pAP->getProperty("text-position", szValue)) && !UT_strcmp(szValue, "subscript")) { props += "style:text-position=\"sub 58%\" "; } if ((pAP->getProperty("font-style", szValue)) && !UT_strcmp(szValue, "italic")) { props += "fo:font-style=\"italic\" "; } if ((pAP->getProperty("font-weight", szValue)) && !UT_strcmp(szValue, "bold") ) { props += "fo:font-weight=\"bold\" "; } if ((pAP->getProperty("text-decoration", szValue))) { const XML_Char* pszDecor = szValue; XML_Char* p; if (!UT_cloneString(static_cast(p), pszDecor)) { // TODO outofmem } UT_return_if_fail(p || !pszDecor); XML_Char* q = strtok(p, " "); while (q) { if (0 == UT_strcmp(q, "line-through")) { props += "style:text-crossing-out=\"single-line\" "; } q = strtok(NULL, " "); } free(p); } if ( (pAP->getProperty("text-decoration", szValue)) ) { const XML_Char* pszDecor = szValue; XML_Char* p; if (!UT_cloneString(static_cast(p), pszDecor)) { // TODO outofmem } UT_return_if_fail(p || !pszDecor); XML_Char* q = strtok(p, " "); while (q) { if (0 == UT_strcmp(q, "underline")) { props += "style:text-underline=\"single\" "; } q = strtok(NULL, " "); } free(p); } if ( (pAP->getProperty("color", szValue)) || (pAP->getProperty("font-size", szValue)) || (pAP->getProperty("font-family", szValue)) || (pAP->getProperty("bgcolor", szValue)) ) { const XML_Char* pszColor = NULL; const XML_Char* pszBgColor = NULL; const XML_Char* pszFontSize = NULL; const XML_Char* pszFontFamily = NULL; pAP->getProperty("color", pszColor); pAP->getProperty("font-size", pszFontSize); pAP->getProperty("font-family", pszFontFamily); pAP->getProperty("bgcolor", pszBgColor); if (pszColor) { // ... } if (pszFontSize) { setlocale (LC_NUMERIC, "C"); UT_String fontSizeProps; fontSizeProps = UT_String_sprintf(fontSizeProps, "fo:font-size=\"%gpt\" ", UT_convertToPoints(pszFontSize)); props += fontSizeProps; setlocale (LC_NUMERIC, ""); } if (pszFontFamily) { // ... } if (pszBgColor) { // ... } } //m_pAP_Span = pAP; } m_pListenerImpl->openSpan(props); m_bInSpan = true; } void OO_Listener::_closeSpan() { if (m_bInSpan) m_pListenerImpl->closeSpan(); m_bInSpan = false; } /*****************************************************************************/ /*****************************************************************************/ /*! * Class holding 1 static member. Its sole duty is to write * out a OOo meta.xml file based on Abi's metadata. */ class OO_MetaDataWriter { public: static bool writeMetaData(PD_Document * pDoc, GsfOutfile * oo) { GsfOutput * meta = gsf_outfile_new_child (oo, "meta.xml", FALSE); static const char * const preamble [] = { "\n", "\n", "\n", "\n", "AbiWord\n" }; static const char * const postamble [] = { "\n", "\n" }; writeToStream(meta, preamble, NrElements(preamble)); UT_UTF8String meta_val, val; if (pDoc->getMetaDataProp(PD_META_KEY_DATE, meta_val) && meta_val.size()) { val = UT_UTF8String_sprintf("%s\n", meta_val.utf8_str()); gsf_output_write(meta, val.size(), reinterpret_cast(val.utf8_str())); } if (pDoc->getMetaDataProp(PD_META_KEY_LANGUAGE, meta_val) && meta_val.size()) { val = UT_UTF8String_sprintf("%s\n", meta_val.utf8_str()); gsf_output_write(meta, val.size(), reinterpret_cast(val.utf8_str())); } writeToStream(meta, postamble, NrElements(postamble)); gsf_output_close(meta); g_object_unref(G_OBJECT(meta)); return true; } private: OO_MetaDataWriter (); }; /*****************************************************************************/ /*****************************************************************************/ /*! * Class holding 1 static member. Its sole duty is to write * out a OOo settings file. Probably will just dump "standard" * info to the settings file, since Abi pretty much ignores * OOo's settings file on import. */ class OO_SettingsWriter { public: static bool writeSettings(PD_Document * pDoc, GsfOutfile * oo) { GsfOutput * settings = gsf_outfile_new_child (oo, "settings.xml", FALSE); static const char * const preamble [] = { "\n", "\n", "\n", "\n", "\n", "" }; writeToStream (settings, preamble, NrElements(preamble)); gsf_output_close(settings); g_object_unref(G_OBJECT(settings)); return true; } private: OO_SettingsWriter (); }; /*****************************************************************************/ /*****************************************************************************/ /*! * Class holding 1 static member. Its sole duty is to write * out any pictures from inside the Abi document to the * OOo pictures directory */ class OO_PicturesWriter { public: static bool writePictures(PD_Document * pDoc, GsfOutfile * oo) { const char * szName; const char * szMimeType; const UT_ByteBuf * pByteBuf; // create Pictures directory GsfOutput * pictures = gsf_outfile_new_child(oo, "Pictures", TRUE); for (UT_uint32 k=0; (pDoc->enumDataItems(k,NULL,&szName,&pByteBuf,reinterpret_cast(const_cast(&szMimeType)))); k++) { // create individual pictures UT_String name = UT_String_sprintf("IMG-%d.png", k); GsfOutput * img = gsf_outfile_new_child(GSF_OUTFILE(pictures), name.c_str(), FALSE); gsf_output_write(img, pByteBuf->getLength(), pByteBuf->getPointer(0)); gsf_output_close(img); g_object_unref(G_OBJECT(img)); } gsf_output_close(pictures); g_object_unref(G_OBJECT(pictures)); return true; } private: OO_PicturesWriter (); }; /*****************************************************************************/ /*****************************************************************************/ /*! * Class holding 1 static member. Its sole duty is to create * the OOo manifest file */ class OO_ManifestWriter { public: static bool writeManifest(PD_Document * pDoc, GsfOutfile * oo) { // create Pictures directory GsfOutput * meta_inf = gsf_outfile_new_child(oo, "META-INF", TRUE); GsfOutput * manifest = gsf_outfile_new_child(GSF_OUTFILE(meta_inf), "manifest.xml", FALSE); UT_String name; static const char * const preamble [] = { "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" }; static const char * const postamble [] = { "\n" }; writeToStream (manifest, preamble, NrElements(preamble)); const char * szName; const char * szMimeType; const UT_ByteBuf * pByteBuf; for (UT_uint32 k = 0; (pDoc->enumDataItems(k,NULL,&szName,&pByteBuf,reinterpret_cast(const_cast(&szMimeType)))); k++) { if (k == 0) { name = "\n"; gsf_output_write(manifest, name.size(), reinterpret_cast(name.c_str())); } name = UT_String_sprintf("\n", k, szMimeType); gsf_output_write (manifest, name.size(), reinterpret_cast(name.c_str())); } writeToStream (manifest, postamble, NrElements(postamble)); gsf_output_close(manifest); g_object_unref(G_OBJECT(manifest)); gsf_output_close(meta_inf); g_object_unref(G_OBJECT(meta_inf)); return true; } private: OO_ManifestWriter (); }; /*****************************************************************************/ /*****************************************************************************/ /*! * Class holding 1 static member. Its sole duty is to write * out a OOo styles file. For now, we just dump "standard" * info to the settings file, putting the actual styles definitions * in the section of the content. */ class OO_StylesWriter { public: static bool writeStyles(PD_Document * pDoc, GsfOutfile * oo) { GsfOutput * styleStream = gsf_outfile_new_child (oo, "styles.xml", FALSE); static const char * const preamble [] = { // TODO! "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" }; writeToStream(styleStream, preamble, NrElements(preamble)); gsf_output_close(styleStream); g_object_unref(G_OBJECT(styleStream)); return true; } private: OO_StylesWriter (); }; /*****************************************************************************/ /*****************************************************************************/ IE_Exp_OpenWriter::IE_Exp_OpenWriter (PD_Document * pDoc) : IE_Exp (pDoc), m_oo(0) { } IE_Exp_OpenWriter::~IE_Exp_OpenWriter() { } /*! * This writes out our AbiWord file as an OpenOffice * compound document */ UT_Error IE_Exp_OpenWriter::_writeDocument(void) { UT_return_val_if_fail(m_oo, UT_ERROR); if (!OO_MetaDataWriter::writeMetaData(getDoc(), m_oo)) return UT_ERROR; if (!OO_SettingsWriter::writeSettings(getDoc(), m_oo)) return UT_ERROR; if (!OO_PicturesWriter::writePictures(getDoc(), m_oo)) return UT_ERROR; if (!OO_ManifestWriter::writeManifest(getDoc(), m_oo)) return UT_ERROR; if (!OO_StylesWriter::writeStyles(getDoc(), m_oo)) return UT_ERROR; OO_StylesContainer stylesContainer; OO_AccumulatorImpl accumulatorImpl(&stylesContainer); OO_Listener listener1(getDoc(), this, &accumulatorImpl); if (!getDoc()->tellListener(static_cast(&listener1))) return UT_ERROR; OO_WriterImpl writerImpl(m_oo, &stylesContainer); OO_Listener listener2(getDoc(), this, &writerImpl); if (!getDoc()->tellListener(static_cast(&listener2))) return UT_ERROR; listener2.endDocument(); return UT_OK; } /*! * Open the underlying ZIP file for writing */ bool IE_Exp_OpenWriter::_openFile(const char * szFilename) { UT_return_val_if_fail(!m_oo, false); GsfOutput * sink = GSF_OUTPUT (gsf_output_stdio_new (szFilename, NULL)); if (!sink) return false; m_oo = GSF_OUTFILE (gsf_outfile_zip_new (sink, NULL)); g_object_unref(G_OBJECT(sink)); return (m_oo != 0); } /*! * Close our underlying ZIP file */ bool IE_Exp_OpenWriter::_closeFile(void) { if(m_oo) { gsf_output_close(GSF_OUTPUT(m_oo)); g_object_unref(G_OBJECT(m_oo)); m_oo = 0; } return true; } /*****************************************************************************/ /*****************************************************************************/