/**
 * 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()));
            
        }
    }
}
Beispiel #2
0
/**
 * 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;
}