/** * Ensure that the manifest:file-entry XML elements exist for all the parent * directories for path. If such an element already has been written (according to * pathsAlreadyWritten) then it will not be done again. */ void ODe_ManifestWriter::ensureDirectoryManifest( PD_Document* pDoc, GsfOutput* manifest, const std::string& path, std::set< std::string >& pathsAlreadyWritten ) { std::vector<std::string> directories; boost::split(directories, path, boost::is_any_of("/")); if( !directories.empty() ) { directories.pop_back(); } std::string runningPath; for( std::vector<std::string>::iterator iter = directories.begin(); iter != directories.end(); ++iter ) { runningPath = runningPath + *iter + "/"; if( !pathsAlreadyWritten.count( runningPath ) ) { pathsAlreadyWritten.insert( runningPath ); std::string name = UT_std_string_sprintf( " <manifest:file-entry manifest:media-type=\"\" manifest:full-path=\"%s\"/>\n", runningPath.c_str() ); ODe_gsf_output_write(manifest, name.size(), reinterpret_cast<const guint8 *>(name.c_str())); } } }
/** * Convert the RDF contained in pDoc->getDocumenrRDF() to RDF/XML and * store that in manifest.rdf updating the pDoc so that a manifest * entry is created in META_INF by the manifest writing code. */ bool ODe_RDFWriter::writeRDF( PD_Document* pDoc, GsfOutfile* pODT, PD_RDFModelHandle additionalRDF ) { #ifndef WITH_REDLAND UT_UNUSED(pDoc); UT_UNUSED(pODT); UT_UNUSED(additionalRDF); return true; #else UT_DEBUGMSG(("writeRDF() \n")); GsfOutput* oss = gsf_outfile_new_child(GSF_OUTFILE(pODT), "manifest.rdf", FALSE); // // Convert the native RDF model into a redland one // PD_DocumentRDFHandle rdf = pDoc->getDocumentRDF(); std::list< PD_RDFModelHandle > ml; ml.push_back( rdf ); ml.push_back( additionalRDF ); std::string rdfxml = toRDFXML( ml ); ODe_gsf_output_write (oss, rdfxml.size(), (const guint8*)rdfxml.data() ); ODe_gsf_output_close(oss); // // add an entry that the manifest writing code will pick up // { UT_ByteBufPtr pByteBuf(new UT_ByteBuf); std::string mime_type = "application/rdf+xml"; PD_DataItemHandle* ppHandle = NULL; if(!pDoc->createDataItem("manifest.rdf", 0, pByteBuf, mime_type, ppHandle)) { UT_DEBUGMSG(("writeRDF() setting up manifest entry failed!\n")); } // This is to test to new dnode manifest code. // pDoc->createDataItem( "some/many/directories/foo.xml", 0, &pByteBuf, // mime_type, ppHandle ); } UT_DEBUGMSG(("writeRDF() complete\n")); return true; #endif }
/** * Writes all pictures inside the Pictures subdirectory. */ bool ODe_PicturesWriter::writePictures(PD_Document* pDoc, GsfOutfile* pODT) { const char * szName; std::string mimeType; std::string extension; std::string fullName; const UT_ByteBuf * pByteBuf; GsfOutput* pImg; GsfOutput* pPicsDir = NULL; for (UT_uint32 k=0; (pDoc->enumDataItems(k, NULL, &szName, &pByteBuf, &mimeType)); k++) { // We must avoid saving RDF data as image if (!mimeType.empty() && (mimeType != "application/rdf+xml")) { if (pPicsDir == NULL) { // create Pictures directory pPicsDir = gsf_outfile_new_child(pODT, "Pictures", TRUE); } pDoc->getDataItemFileExtension(szName, extension, true); fullName = szName + extension; pImg = gsf_outfile_new_child(GSF_OUTFILE(pPicsDir), fullName.c_str(), FALSE); ODe_gsf_output_write(pImg, pByteBuf->getLength(), pByteBuf->getPointer(0)); ODe_gsf_output_close(pImg); } } if (pPicsDir != NULL) { ODe_gsf_output_close(pPicsDir); } return true; }
bool ODe_Style_MasterPage::write(GsfOutput* pODT) const { UT_UTF8String output; UT_UTF8String_sprintf(output, " <style:master-page style:name=\"%s\" style:page-layout-name=\"%s\">\n", m_name.utf8_str(), m_pageLayoutName.utf8_str()); ODe_writeUTF8String(pODT, output); /* We have to deal with two confusion things when writing out header/footers: 1. Oddly enough AbiWord uses "header-even" and "footer-even" for page 1, 3, 5, etc :) 2. In OpenDocument you can specify an alternative header/footer for "left" pages. Oddly enough OpenOffice.org seems to interpret "left" pages as page 2, 4, 6, etc. */ if (!m_abiHeaderId.empty()) { // It has a header ODe_writeUTF8String(pODT, " <style:header>\n"); // Swap even/uneven when there is an alternative header for uneven pages to // match what OpenOffice expects if (m_abiHeaderEvenId.empty()) { ODe_gsf_output_write(pODT, gsf_output_size (m_pHeaderContentTemp), gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (m_pHeaderContentTemp))); } else { ODe_gsf_output_write(pODT, gsf_output_size (m_pHeaderEvenContentTemp), gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (m_pHeaderEvenContentTemp))); } ODe_writeUTF8String(pODT, " </style:header>\n"); } if (!m_abiHeaderEvenId.empty()) { // It has a different header for uneven pages ODe_writeUTF8String(pODT, " <style:header-left>\n"); ODe_gsf_output_write(pODT, gsf_output_size (m_pHeaderContentTemp), gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (m_pHeaderContentTemp))); ODe_writeUTF8String(pODT, " </style:header-left>\n"); } if (!m_abiFooterId.empty()) { // It has a footer ODe_writeUTF8String(pODT, " <style:footer>\n"); // Swap even/uneven when there is an alternative footer for uneven pages to // match what OpenOffice expects if (m_abiFooterEvenId.empty()) { ODe_gsf_output_write(pODT, gsf_output_size (m_pFooterContentTemp), gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (m_pFooterContentTemp))); } else { ODe_gsf_output_write(pODT, gsf_output_size (m_pFooterEvenContentTemp), gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (m_pFooterEvenContentTemp))); } ODe_writeUTF8String(pODT, " </style:footer>\n"); } if (!m_abiFooterEvenId.empty()) { // It has a footer for uneven pages ODe_writeUTF8String(pODT, " <style:footer-left>\n"); ODe_gsf_output_write(pODT, gsf_output_size (m_pFooterContentTemp), gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (m_pFooterContentTemp))); ODe_writeUTF8String(pODT, " </style:footer-left>\n"); } ODe_writeUTF8String(pODT, " </style:master-page>\n"); return true; }
/** * This writes out our AbiWord file as an OpenOffice * compound document */ UT_Error IE_Exp_OpenDocument::_writeDocument(void) { ODe_DocumentData docData(getDoc()); ODe_AuxiliaryData auxData; ODe_AbiDocListener* pAbiDocListener = NULL; ODe_AbiDocListenerImpl* pAbiDocListenerImpl = NULL; UT_return_val_if_fail (getFp(), UT_ERROR); PD_DocumentRDFHandle rdf = getDoc()->getDocumentRDF(); auxData.m_additionalRDF = rdf->createScratchModel(); const std::string & prop = getProperty ("uncompressed"); if (!prop.empty() && UT_parseBool (prop.c_str (), false)) { m_odt = GSF_OUTFILE(g_object_ref(G_OBJECT(getFp()))); } else { GError* error = NULL; m_odt = GSF_OUTFILE (gsf_outfile_zip_new (getFp(), &error)); if (error) { UT_DEBUGMSG(("Error writing odt file: %s\n", error->message)); UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); } } UT_return_val_if_fail(m_odt, UT_ERROR); // Needed to ensure that all *printf writes numbers correctly, // like "45.56mm" instead of "45,56mm". UT_LocaleTransactor numericLocale (LC_NUMERIC, "C"); { GsfOutput * mimetype = gsf_outfile_new_child_full (m_odt, "mimetype", FALSE, "compression-level", 0, (void*)0); if (!mimetype) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } ODe_gsf_output_write(mimetype, 39 /*39 == strlen("application/vnd.oasis.opendocument.text")*/, (const guint8 *)"application/vnd.oasis.opendocument.text"); ODe_gsf_output_close(mimetype); } if (!ODe_MetaDataWriter::writeMetaData(getDoc(), m_odt)) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } if (!ODe_SettingsWriter::writeSettings(getDoc(), m_odt)) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } if (!ODe_PicturesWriter::writePictures(getDoc(), m_odt)) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } if (!ODe_ManifestWriter::writeManifest(getDoc(), m_odt)) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } // Gather all paragraph style names used by heading paragraphs // (ie. all paragraph styles that are used to build up TOCs). pAbiDocListenerImpl = new ODe_HeadingSearcher_Listener(docData.m_styles, auxData); pAbiDocListener = new ODe_AbiDocListener(getDoc(), pAbiDocListenerImpl, false); if (!getDoc()->tellListener(static_cast<PL_Listener *>(pAbiDocListener))) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } pAbiDocListener->finished(); DELETEP(pAbiDocListener); DELETEP(pAbiDocListenerImpl); // Now that we have all paragraph styles that build up the TOCs in the // document (if any), we can build up the TOC bodies. We do this because // OpenOffice.org requires the TOC bodies to be present and filled // when initially opening the document. Without it, it will show // an empty TOC until the user regenerates it, which is not that pretty. // Annoyingly we have to build up the TOC ourselves during export, as // it doesn't exist within AbiWord's PieceTable. Until that changes, this // is the best we can do. if (auxData.m_pTOCContents) { pAbiDocListenerImpl = new ODe_TOC_Listener(auxData); pAbiDocListener = new ODe_AbiDocListener(getDoc(), pAbiDocListenerImpl, false); if (!getDoc()->tellListener(static_cast<PL_Listener *>(pAbiDocListener))) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } pAbiDocListener->finished(); DELETEP(pAbiDocListener); DELETEP(pAbiDocListenerImpl); } // Gather document content and styles if (!docData.doPreListeningWork()) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } pAbiDocListenerImpl = new ODe_Main_Listener(docData, auxData); pAbiDocListener = new ODe_AbiDocListener(getDoc(), pAbiDocListenerImpl, false); if (!getDoc()->tellListener(static_cast<PL_Listener *>(pAbiDocListener))) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } pAbiDocListener->finished(); DELETEP(pAbiDocListener); DELETEP(pAbiDocListenerImpl); if (!docData.doPostListeningWork()) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } // Write RDF. if (!ODe_RDFWriter::writeRDF(getDoc(), m_odt, auxData.m_additionalRDF )) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } // Write content and styles if (!docData.writeStylesXML(m_odt)) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } if (!docData.writeContentXML(m_odt)) { ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_ERROR; } ODe_gsf_output_close(GSF_OUTPUT(m_odt)); return UT_OK; }
bool ODe_DocumentData::writeContentXML(GsfOutfile* pOdt) { GsfOutput* pContentStream; pContentStream = gsf_outfile_new_child (pOdt, "content.xml", FALSE); const char * const preamble [] = { "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", "\n", "<office:document-content" " xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"" " xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"" " xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"" " xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"" " xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"" " xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"" " xmlns:xlink=\"http://www.w3.org/1999/xlink\"" " xmlns:dc=\"http://purl.org/dc/elements/1.1/\"" " xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"" " xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"" " xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"" " xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"" " xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"" " xmlns:math=\"http://www.w3.org/1998/Math/MathML\"" " xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"" " xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"" " xmlns:ooo=\"http://openoffice.org/2004/office\"" " xmlns:ooow=\"http://openoffice.org/2004/writer\"" " xmlns:oooc=\"http://openoffice.org/2004/calc\"" " xmlns:dom=\"http://www.w3.org/2001/xml-events\"" " xmlns:xforms=\"http://www.w3.org/2002/xforms\"" " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"" " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" " xmlns:delta=\"http://www.deltaxml.com/ns/track-changes/delta-namespace\"" " xmlns:ac=\"http://www.deltaxml.com/ns/track-changes/attribute-change-namespace\"" " xmlns:split=\"http://www.deltaxml.com/ns/track-changes/split-namespace\"" " office:version=\"1.1\">\n"}; ODe_writeToStream(pContentStream, preamble, G_N_ELEMENTS(preamble)); m_contentXMLFontDecls.write( pContentStream ); m_contentAutoStyles.write( pContentStream ); m_contentRevisions.write( pContentStream, m_pAbiDoc ); ODe_writeUTF8String(pContentStream, " <office:body>\n" " <office:text>\n"); // FIXME: here OR the one above? // m_contentRevisions.write( pContentStream, m_pAbiDoc ); ODe_gsf_output_write(pContentStream, gsf_output_size (m_pOfficeTextTemp), gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (m_pOfficeTextTemp))); ODe_gsf_output_close (m_pOfficeTextTemp); m_pOfficeTextTemp = NULL; ODe_writeUTF8String(pContentStream, " </office:text>\n" " </office:body>\n" "</office:document-content>"); ODe_gsf_output_close(pContentStream); return true; }
bool ODe_ManifestWriter::writeManifest(PD_Document* pDoc, GsfOutfile* pODT) { // Create META-INF directory GsfOutput* meta_inf = gsf_outfile_new_child(pODT, "META-INF", TRUE); GsfOutput* manifest = gsf_outfile_new_child( GSF_OUTFILE(meta_inf), "manifest.xml", FALSE); std::string name; static const char * const preamble [] = { "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", "<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n", "<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n", " <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.text\" manifest:full-path=\"/\"/>\n", " <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n", " <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"styles.xml\"/>\n", " <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n", " <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"settings.xml\"/>\n" }; static const char * const postamble [] = { "</manifest:manifest>\n" }; typedef std::set< std::string > absolutePathMimeTypes_t; static absolutePathMimeTypes_t absolutePathMimeTypes; if( absolutePathMimeTypes.empty() ) { absolutePathMimeTypes.insert("application/rdf+xml"); } ODe_writeToStream (manifest, preamble, G_N_ELEMENTS(preamble)); const char* szName; std::string mimeType; const UT_ByteBuf* pByteBuf; std::set< std::string > pathsAlreadyWritten; for (UT_uint32 k = 0; (pDoc->enumDataItems(k, NULL, &szName, &pByteBuf, &mimeType)); k++) { if (!mimeType.empty()) { ensureDirectoryManifest( pDoc, manifest, szName, pathsAlreadyWritten ); std::string automaticPathPrefix = "Pictures/"; if( absolutePathMimeTypes.count(mimeType) ) automaticPathPrefix = ""; name = UT_std_string_sprintf( " <manifest:file-entry manifest:media-type=\"%s\" manifest:full-path=\"%s%s\"/>\n", mimeType.c_str(), automaticPathPrefix.c_str(), szName); ODe_gsf_output_write (manifest, name.size(), reinterpret_cast<const guint8 *>(name.c_str())); } } ODe_writeToStream (manifest, postamble, G_N_ELEMENTS(postamble)); ODe_gsf_output_close(manifest); ODe_gsf_output_close(meta_inf); return true; }