/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: xmlmetae.cxx,v $ * $Revision: 1.27 $ * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org 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 Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_xmloff.hxx" #include #include #include #include #include #include #include #include #include #include #include "xmlnmspe.hxx" #include #include #include #include #include #include #include using namespace com::sun::star; using namespace ::xmloff::token; //------------------------------------------------------------------------- void lcl_AddTwoDigits( rtl::OUStringBuffer& rStr, sal_Int32 nVal ) { if ( nVal < 10 ) rStr.append( sal_Unicode('0') ); rStr.append( nVal ); } rtl::OUString SvXMLMetaExport::GetISODateTimeString( const util::DateTime& rDateTime ) { // return ISO date string "YYYY-MM-DDThh:mm:ss" rtl::OUStringBuffer sTmp; sTmp.append( (sal_Int32) rDateTime.Year ); sTmp.append( sal_Unicode('-') ); lcl_AddTwoDigits( sTmp, rDateTime.Month ); sTmp.append( sal_Unicode('-') ); lcl_AddTwoDigits( sTmp, rDateTime.Day ); sTmp.append( sal_Unicode('T') ); lcl_AddTwoDigits( sTmp, rDateTime.Hours ); sTmp.append( sal_Unicode(':') ); lcl_AddTwoDigits( sTmp, rDateTime.Minutes ); sTmp.append( sal_Unicode(':') ); lcl_AddTwoDigits( sTmp, rDateTime.Seconds ); return sTmp.makeStringAndClear(); } //------------------------------------------------------------------------- void SvXMLMetaExport::SimpleStringElement( const rtl::OUString& rText, sal_uInt16 nNamespace, enum XMLTokenEnum eElementName ) { if ( rText.getLength() ) { SvXMLElementExport aElem( mrExport, nNamespace, eElementName, sal_True, sal_False ); mrExport.Characters( rText ); } } void SvXMLMetaExport::SimpleDateTimeElement( const util::DateTime & rDate, sal_uInt16 nNamespace, enum XMLTokenEnum eElementName ) { if (rDate.Month != 0) { // invalid dates are 0-0-0 rtl::OUString sValue = GetISODateTimeString( rDate ); if ( sValue.getLength() ) { SvXMLElementExport aElem( mrExport, nNamespace, eElementName, sal_True, sal_False ); mrExport.Characters( sValue ); } } } void SvXMLMetaExport::_MExport() { // generator { SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_GENERATOR, sal_True, sal_True ); mrExport.Characters( ::utl::DocInfoHelper::GetGeneratorString() ); } // document title SimpleStringElement ( mxDocProps->getTitle(), XML_NAMESPACE_DC, XML_TITLE ); // description SimpleStringElement ( mxDocProps->getDescription(), XML_NAMESPACE_DC, XML_DESCRIPTION ); // subject SimpleStringElement ( mxDocProps->getSubject(), XML_NAMESPACE_DC, XML_SUBJECT ); // created... SimpleStringElement ( mxDocProps->getAuthor(), XML_NAMESPACE_META, XML_INITIAL_CREATOR ); SimpleDateTimeElement( mxDocProps->getCreationDate(), XML_NAMESPACE_META, XML_CREATION_DATE ); // modified... SimpleStringElement ( mxDocProps->getModifiedBy(), XML_NAMESPACE_DC, XML_CREATOR ); SimpleDateTimeElement( mxDocProps->getModificationDate(), XML_NAMESPACE_DC, XML_DATE ); // printed... SimpleStringElement ( mxDocProps->getPrintedBy(), XML_NAMESPACE_META, XML_PRINTED_BY ); SimpleDateTimeElement( mxDocProps->getPrintDate(), XML_NAMESPACE_META, XML_PRINT_DATE ); // keywords const uno::Sequence< ::rtl::OUString > keywords = mxDocProps->getKeywords(); for (sal_Int32 i = 0; i < keywords.getLength(); ++i) { SvXMLElementExport aKwElem( mrExport, XML_NAMESPACE_META, XML_KEYWORD, sal_True, sal_False ); mrExport.Characters( keywords[i] ); } // document language { const lang::Locale aLocale = mxDocProps->getLanguage(); ::rtl::OUString sValue = aLocale.Language; if (sValue.getLength()) { if ( aLocale.Country.getLength() ) { sValue += rtl::OUString::valueOf((sal_Unicode)'-'); sValue += aLocale.Country; } SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DC, XML_LANGUAGE, sal_True, sal_False ); mrExport.Characters( sValue ); } } // editing cycles { SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_EDITING_CYCLES, sal_True, sal_False ); mrExport.Characters( ::rtl::OUString::valueOf( static_cast(mxDocProps->getEditingCycles()) ) ); } // editing duration // property is a int32 (seconds) { sal_Int32 secs = mxDocProps->getEditingDuration(); SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_EDITING_DURATION, sal_True, sal_False ); mrExport.Characters( SvXMLUnitConverter::convertTimeDuration( Time(secs/3600, (secs%3600)/60, secs%60)) ); } // default target const ::rtl::OUString sDefTarget = mxDocProps->getDefaultTarget(); if ( sDefTarget.getLength() ) { mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME, sDefTarget ); //! define strings for xlink:show values const XMLTokenEnum eShow = sDefTarget.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_blank")) ? XML_NEW : XML_REPLACE; mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, eShow ); SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META,XML_HYPERLINK_BEHAVIOUR, sal_True, sal_False ); } // auto-reload const ::rtl::OUString sReloadURL = mxDocProps->getAutoloadURL(); const sal_Int32 sReloadDelay = mxDocProps->getAutoloadSecs(); if (sReloadDelay != 0 || sReloadURL.getLength() != 0) { mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, mrExport.GetRelativeReference( sReloadURL ) ); mrExport.AddAttribute( XML_NAMESPACE_META, XML_DELAY, SvXMLUnitConverter::convertTimeDuration( Time(sReloadDelay/3600, (sReloadDelay%3600)/60, sReloadDelay%60 )) ); SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_AUTO_RELOAD, sal_True, sal_False ); } // template const rtl::OUString sTplPath = mxDocProps->getTemplateURL(); if ( sTplPath.getLength() ) { mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONREQUEST ); // template URL mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, mrExport.GetRelativeReference(sTplPath) ); // template name mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TITLE, mxDocProps->getTemplateName() ); // template date mrExport.AddAttribute( XML_NAMESPACE_META, XML_DATE, GetISODateTimeString( mxDocProps->getTemplateDate() ) ); SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_TEMPLATE, sal_True, sal_False ); } // user defined fields uno::Reference< beans::XPropertyAccess > xUserDefined( mxDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW); const uno::Sequence< beans::PropertyValue > props = xUserDefined->getPropertyValues(); for (sal_Int32 i = 0; i < props.getLength(); ++i) { ::rtl::OUStringBuffer sValueBuffer; ::rtl::OUStringBuffer sType; if (!SvXMLUnitConverter::convertAny( sValueBuffer, sType, props[i].Value)) { continue; } mrExport.AddAttribute( XML_NAMESPACE_META, XML_NAME, props[i].Name ); mrExport.AddAttribute( XML_NAMESPACE_META, XML_VALUE_TYPE, sType.makeStringAndClear() ); SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_USER_DEFINED, sal_True, sal_False ); mrExport.Characters( sValueBuffer.makeStringAndClear() ); } const uno::Sequence< beans::NamedValue > aDocStatistic = mxDocProps->getDocumentStatistics(); // write document statistic if there is any provided if ( aDocStatistic.getLength() ) { for ( sal_Int32 nInd = 0; nInd < aDocStatistic.getLength(); nInd++ ) { sal_Int32 nValue = 0; if ( aDocStatistic[nInd].Value >>= nValue ) { ::rtl::OUString aValue = rtl::OUString::valueOf( nValue ); if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TableCount" ) ) ) ) mrExport.AddAttribute( XML_NAMESPACE_META, XML_TABLE_COUNT, aValue ); else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ObjectCount" ) ) ) ) mrExport.AddAttribute( XML_NAMESPACE_META, XML_OBJECT_COUNT, aValue ); else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ImageCount" ) ) ) ) mrExport.AddAttribute( XML_NAMESPACE_META, XML_IMAGE_COUNT, aValue ); else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PageCount" ) ) ) ) mrExport.AddAttribute( XML_NAMESPACE_META, XML_PAGE_COUNT, aValue ); else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ParagraphCount" ) ) ) ) mrExport.AddAttribute( XML_NAMESPACE_META, XML_PARAGRAPH_COUNT, aValue ); else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "WordCount" ) ) ) ) mrExport.AddAttribute( XML_NAMESPACE_META, XML_WORD_COUNT, aValue ); else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CharacterCount" ) ) ) ) mrExport.AddAttribute( XML_NAMESPACE_META, XML_CHARACTER_COUNT, aValue ); else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CellCount" ) ) ) ) mrExport.AddAttribute( XML_NAMESPACE_META, XML_CELL_COUNT, aValue ); else { DBG_ASSERT( sal_False, "Unknown statistic value!\n" ); } } } SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_DOCUMENT_STATISTIC, sal_True, sal_True ); } } //------------------------------------------------------------------------- static const char *s_xmlns = "xmlns"; static const char *s_xmlns2 = "xmlns:"; static const char *s_meta = "meta:"; static const char *s_href = "xlink:href"; SvXMLMetaExport::SvXMLMetaExport( SvXMLExport& i_rExp, const uno::Reference& i_rDocProps ) : mrExport( i_rExp ), mxDocProps( i_rDocProps ), m_level( 0 ), m_preservedNSs() { DBG_ASSERT( mxDocProps.is(), "no document properties" ); } SvXMLMetaExport::~SvXMLMetaExport() { } void SvXMLMetaExport::Export() { // exportDom(xDOM, mrExport); // this would not work (root node, namespaces) uno::Reference< xml::sax::XSAXSerializable> xSAXable(mxDocProps, uno::UNO_QUERY); if (xSAXable.is()) { ::comphelper::SequenceAsVector< beans::StringPair > namespaces; const SvXMLNamespaceMap & rNsMap(mrExport.GetNamespaceMap()); for (sal_uInt16 key = rNsMap.GetFirstKey(); key != USHRT_MAX; key = rNsMap.GetNextKey(key)) { beans::StringPair ns; const ::rtl::OUString attrname = rNsMap.GetAttrNameByKey(key); if (attrname.matchAsciiL(s_xmlns2, strlen(s_xmlns2))) { ns.First = attrname.copy(strlen(s_xmlns2)); } else if (attrname.equalsAsciiL(s_xmlns, strlen(s_xmlns))) { // default initialized empty string } else { DBG_ERROR("namespace attribute not starting with xmlns unexpected"); } ns.Second = rNsMap.GetNameByKey(key); namespaces.push_back(ns); } xSAXable->serialize(this, namespaces.getAsConstList()); } else { // office:meta SvXMLElementExport aElem( mrExport, XML_NAMESPACE_OFFICE, XML_META, sal_True, sal_True ); // fall back to using public interface of XDocumentProperties _MExport(); } } // ::com::sun::star::xml::sax::XDocumentHandler: void SAL_CALL SvXMLMetaExport::startDocument() throw (uno::RuntimeException, xml::sax::SAXException) { // ignore: has already been done by SvXMLExport::exportDoc DBG_ASSERT( m_level == 0, "SvXMLMetaExport: level error" ); } void SAL_CALL SvXMLMetaExport::endDocument() throw (uno::RuntimeException, xml::sax::SAXException) { // ignore: will be done by SvXMLExport::exportDoc DBG_ASSERT( m_level == 0, "SvXMLMetaExport: level error" ); } // unfortunately, this method contains far too much ugly namespace mangling. void SAL_CALL SvXMLMetaExport::startElement(const ::rtl::OUString & i_rName, const uno::Reference< xml::sax::XAttributeList > & i_xAttribs) throw (uno::RuntimeException, xml::sax::SAXException) { if (m_level == 0) { // namepace decls: default ones have been written at the root element // non-default ones must be preserved here const sal_Int16 nCount = i_xAttribs->getLength(); for (sal_Int16 i = 0; i < nCount; ++i) { const ::rtl::OUString name(i_xAttribs->getNameByIndex(i)); if (name.matchAsciiL(s_xmlns, strlen(s_xmlns))) { bool found(false); const SvXMLNamespaceMap & rNsMap(mrExport.GetNamespaceMap()); for (sal_uInt16 key = rNsMap.GetFirstKey(); key != USHRT_MAX; key = rNsMap.GetNextKey(key)) { if (name.equals(rNsMap.GetAttrNameByKey(key))) { found = true; break; } } if (!found) { m_preservedNSs.push_back(beans::StringPair(name, i_xAttribs->getValueByIndex(i))); } } } // ignore the root: it has been written already ++m_level; return; } if (m_level == 1) { // attach preserved namespace decls from root node here for (std::vector::const_iterator iter = m_preservedNSs.begin(); iter != m_preservedNSs.end(); ++iter) { const ::rtl::OUString ns(iter->First); bool found(false); // but only if it is not already there const sal_Int16 nCount = i_xAttribs->getLength(); for (sal_Int16 i = 0; i < nCount; ++i) { const ::rtl::OUString name(i_xAttribs->getNameByIndex(i)); if (ns.equals(name)) { found = true; break; } } if (!found) { mrExport.AddAttribute(ns, iter->Second); } } } // attach the attributes if (i_rName.matchAsciiL(s_meta, strlen(s_meta))) { // special handling for all elements that may have // xlink:href attributes; these must be made relative const sal_Int16 nLength = i_xAttribs->getLength(); for (sal_Int16 i = 0; i < nLength; ++i) { const ::rtl::OUString name (i_xAttribs->getNameByIndex (i)); ::rtl::OUString value(i_xAttribs->getValueByIndex(i)); if (name.matchAsciiL(s_href, strlen(s_href))) { value = mrExport.GetRelativeReference(value); } mrExport.AddAttribute(name, value); } } else { const sal_Int16 nLength = i_xAttribs->getLength(); for (sal_Int16 i = 0; i < nLength; ++i) { const ::rtl::OUString name (i_xAttribs->getNameByIndex(i)); const ::rtl::OUString value (i_xAttribs->getValueByIndex(i)); mrExport.AddAttribute(name, value); } } // finally, start the element mrExport.StartElement(i_rName, sal_True); //FIXME:whitespace? ++m_level; } void SAL_CALL SvXMLMetaExport::endElement(const ::rtl::OUString & i_rName) throw (uno::RuntimeException, xml::sax::SAXException) { --m_level; if (m_level == 0) { // ignore the root; see startElement return; } DBG_ASSERT( m_level >= 0, "SvXMLMetaExport: level error" ); mrExport.EndElement(i_rName, sal_False); } void SAL_CALL SvXMLMetaExport::characters(const ::rtl::OUString & i_rChars) throw (uno::RuntimeException, xml::sax::SAXException) { mrExport.Characters(i_rChars); } void SAL_CALL SvXMLMetaExport::ignorableWhitespace(const ::rtl::OUString & /*i_rWhitespaces*/) throw (uno::RuntimeException, xml::sax::SAXException) { mrExport.IgnorableWhitespace(/*i_rWhitespaces*/); } void SAL_CALL SvXMLMetaExport::processingInstruction(const ::rtl::OUString & i_rTarget, const ::rtl::OUString & i_rData) throw (uno::RuntimeException, xml::sax::SAXException) { // ignore; the exporter cannot handle these (void) i_rTarget; (void) i_rData; } void SAL_CALL SvXMLMetaExport::setDocumentLocator(const uno::Reference&) throw (uno::RuntimeException, xml::sax::SAXException) { // nothing to do here, move along... }