1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: xmlmetae.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_xmloff.hxx"
34 #include <tools/debug.hxx>
35 #include <tools/inetdef.hxx>
36 #include <i18npool/mslangid.hxx>
37 #include <tools/urlobj.hxx>
38 #include <tools/time.hxx>
39 #include <rtl/ustrbuf.hxx>
41 #include <xmloff/xmlmetae.hxx>
42 #include <xmloff/xmlexp.hxx>
43 #include <xmloff/xmluconv.hxx>
44 #include <xmloff/nmspmap.hxx>
45 #include "xmlnmspe.hxx"
47 #include <com/sun/star/beans/XPropertyAccess.hpp>
48 #include <com/sun/star/beans/StringPair.hpp>
49 #include <com/sun/star/xml/dom/XDocument.hpp>
50 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
52 #include <comphelper/sequenceasvector.hxx>
53 #include <unotools/docinfohelper.hxx>
58 using namespace com::sun::star
;
59 using namespace ::xmloff::token
;
62 //-------------------------------------------------------------------------
64 void lcl_AddTwoDigits( rtl::OUStringBuffer
& rStr
, sal_Int32 nVal
)
67 rStr
.append( sal_Unicode('0') );
72 SvXMLMetaExport::GetISODateTimeString( const util::DateTime
& rDateTime
)
74 // return ISO date string "YYYY-MM-DDThh:mm:ss"
76 rtl::OUStringBuffer sTmp
;
77 sTmp
.append( (sal_Int32
) rDateTime
.Year
);
78 sTmp
.append( sal_Unicode('-') );
79 lcl_AddTwoDigits( sTmp
, rDateTime
.Month
);
80 sTmp
.append( sal_Unicode('-') );
81 lcl_AddTwoDigits( sTmp
, rDateTime
.Day
);
82 sTmp
.append( sal_Unicode('T') );
83 lcl_AddTwoDigits( sTmp
, rDateTime
.Hours
);
84 sTmp
.append( sal_Unicode(':') );
85 lcl_AddTwoDigits( sTmp
, rDateTime
.Minutes
);
86 sTmp
.append( sal_Unicode(':') );
87 lcl_AddTwoDigits( sTmp
, rDateTime
.Seconds
);
89 return sTmp
.makeStringAndClear();
92 //-------------------------------------------------------------------------
94 void SvXMLMetaExport::SimpleStringElement( const rtl::OUString
& rText
,
95 sal_uInt16 nNamespace
, enum XMLTokenEnum eElementName
)
97 if ( rText
.getLength() ) {
98 SvXMLElementExport
aElem( mrExport
, nNamespace
, eElementName
,
99 sal_True
, sal_False
);
100 mrExport
.Characters( rText
);
104 void SvXMLMetaExport::SimpleDateTimeElement( const util::DateTime
& rDate
,
105 sal_uInt16 nNamespace
, enum XMLTokenEnum eElementName
)
107 if (rDate
.Month
!= 0) { // invalid dates are 0-0-0
108 rtl::OUString sValue
= GetISODateTimeString( rDate
);
109 if ( sValue
.getLength() ) {
110 SvXMLElementExport
aElem( mrExport
, nNamespace
, eElementName
,
111 sal_True
, sal_False
);
112 mrExport
.Characters( sValue
);
117 void SvXMLMetaExport::_MExport()
121 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_META
, XML_GENERATOR
,
122 sal_True
, sal_True
);
123 mrExport
.Characters( ::utl::DocInfoHelper::GetGeneratorString() );
127 SimpleStringElement ( mxDocProps
->getTitle(),
128 XML_NAMESPACE_DC
, XML_TITLE
);
131 SimpleStringElement ( mxDocProps
->getDescription(),
132 XML_NAMESPACE_DC
, XML_DESCRIPTION
);
135 SimpleStringElement ( mxDocProps
->getSubject(),
136 XML_NAMESPACE_DC
, XML_SUBJECT
);
139 SimpleStringElement ( mxDocProps
->getAuthor(),
140 XML_NAMESPACE_META
, XML_INITIAL_CREATOR
);
141 SimpleDateTimeElement( mxDocProps
->getCreationDate(),
142 XML_NAMESPACE_META
, XML_CREATION_DATE
);
145 SimpleStringElement ( mxDocProps
->getModifiedBy(),
146 XML_NAMESPACE_DC
, XML_CREATOR
);
147 SimpleDateTimeElement( mxDocProps
->getModificationDate(),
148 XML_NAMESPACE_DC
, XML_DATE
);
151 SimpleStringElement ( mxDocProps
->getPrintedBy(),
152 XML_NAMESPACE_META
, XML_PRINTED_BY
);
153 SimpleDateTimeElement( mxDocProps
->getPrintDate(),
154 XML_NAMESPACE_META
, XML_PRINT_DATE
);
157 const uno::Sequence
< ::rtl::OUString
> keywords
= mxDocProps
->getKeywords();
158 for (sal_Int32 i
= 0; i
< keywords
.getLength(); ++i
) {
159 SvXMLElementExport
aKwElem( mrExport
, XML_NAMESPACE_META
, XML_KEYWORD
,
160 sal_True
, sal_False
);
161 mrExport
.Characters( keywords
[i
] );
166 const lang::Locale aLocale
= mxDocProps
->getLanguage();
167 ::rtl::OUString sValue
= aLocale
.Language
;
168 if (sValue
.getLength()) {
169 if ( aLocale
.Country
.getLength() )
171 sValue
+= rtl::OUString::valueOf((sal_Unicode
)'-');
172 sValue
+= aLocale
.Country
;
174 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_DC
, XML_LANGUAGE
,
175 sal_True
, sal_False
);
176 mrExport
.Characters( sValue
);
182 SvXMLElementExport
aElem( mrExport
,
183 XML_NAMESPACE_META
, XML_EDITING_CYCLES
,
184 sal_True
, sal_False
);
185 mrExport
.Characters( ::rtl::OUString::valueOf(
186 static_cast<sal_Int32
>(mxDocProps
->getEditingCycles()) ) );
190 // property is a int32 (seconds)
192 sal_Int32 secs
= mxDocProps
->getEditingDuration();
193 SvXMLElementExport
aElem( mrExport
,
194 XML_NAMESPACE_META
, XML_EDITING_DURATION
,
195 sal_True
, sal_False
);
196 mrExport
.Characters( SvXMLUnitConverter::convertTimeDuration(
197 Time(secs
/3600, (secs
%3600)/60, secs
%60)) );
201 const ::rtl::OUString sDefTarget
= mxDocProps
->getDefaultTarget();
202 if ( sDefTarget
.getLength() )
204 mrExport
.AddAttribute( XML_NAMESPACE_OFFICE
, XML_TARGET_FRAME_NAME
,
207 //! define strings for xlink:show values
208 const XMLTokenEnum eShow
=
209 sDefTarget
.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_blank"))
210 ? XML_NEW
: XML_REPLACE
;
211 mrExport
.AddAttribute( XML_NAMESPACE_XLINK
, XML_SHOW
, eShow
);
213 SvXMLElementExport
aElem( mrExport
,
214 XML_NAMESPACE_META
,XML_HYPERLINK_BEHAVIOUR
,
215 sal_True
, sal_False
);
219 const ::rtl::OUString sReloadURL
= mxDocProps
->getAutoloadURL();
220 const sal_Int32 sReloadDelay
= mxDocProps
->getAutoloadSecs();
221 if (sReloadDelay
!= 0 || sReloadURL
.getLength() != 0)
223 mrExport
.AddAttribute( XML_NAMESPACE_XLINK
, XML_HREF
,
224 mrExport
.GetRelativeReference( sReloadURL
) );
226 mrExport
.AddAttribute( XML_NAMESPACE_META
, XML_DELAY
,
227 SvXMLUnitConverter::convertTimeDuration(
228 Time(sReloadDelay
/3600, (sReloadDelay
%3600)/60,
229 sReloadDelay
%60 )) );
231 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_META
, XML_AUTO_RELOAD
,
232 sal_True
, sal_False
);
236 const rtl::OUString sTplPath
= mxDocProps
->getTemplateURL();
237 if ( sTplPath
.getLength() )
239 mrExport
.AddAttribute( XML_NAMESPACE_XLINK
, XML_TYPE
, XML_SIMPLE
);
240 mrExport
.AddAttribute( XML_NAMESPACE_XLINK
, XML_ACTUATE
, XML_ONREQUEST
);
243 mrExport
.AddAttribute( XML_NAMESPACE_XLINK
, XML_HREF
,
244 mrExport
.GetRelativeReference(sTplPath
) );
247 mrExport
.AddAttribute( XML_NAMESPACE_XLINK
, XML_TITLE
,
248 mxDocProps
->getTemplateName() );
251 mrExport
.AddAttribute( XML_NAMESPACE_META
, XML_DATE
,
252 GetISODateTimeString( mxDocProps
->getTemplateDate() ) );
254 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_META
, XML_TEMPLATE
,
255 sal_True
, sal_False
);
258 // user defined fields
259 uno::Reference
< beans::XPropertyAccess
> xUserDefined(
260 mxDocProps
->getUserDefinedProperties(), uno::UNO_QUERY_THROW
);
261 const uno::Sequence
< beans::PropertyValue
> props
=
262 xUserDefined
->getPropertyValues();
263 for (sal_Int32 i
= 0; i
< props
.getLength(); ++i
) {
264 ::rtl::OUStringBuffer sValueBuffer
;
265 ::rtl::OUStringBuffer sType
;
266 if (!SvXMLUnitConverter::convertAny(
267 sValueBuffer
, sType
, props
[i
].Value
)) {
270 mrExport
.AddAttribute( XML_NAMESPACE_META
, XML_NAME
, props
[i
].Name
);
271 mrExport
.AddAttribute( XML_NAMESPACE_META
, XML_VALUE_TYPE
,
272 sType
.makeStringAndClear() );
273 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_META
,
274 XML_USER_DEFINED
, sal_True
, sal_False
);
275 mrExport
.Characters( sValueBuffer
.makeStringAndClear() );
278 const uno::Sequence
< beans::NamedValue
> aDocStatistic
=
279 mxDocProps
->getDocumentStatistics();
280 // write document statistic if there is any provided
281 if ( aDocStatistic
.getLength() )
283 for ( sal_Int32 nInd
= 0; nInd
< aDocStatistic
.getLength(); nInd
++ )
285 sal_Int32 nValue
= 0;
286 if ( aDocStatistic
[nInd
].Value
>>= nValue
)
288 ::rtl::OUString aValue
= rtl::OUString::valueOf( nValue
);
289 if ( aDocStatistic
[nInd
].Name
.equals( ::rtl::OUString(
290 RTL_CONSTASCII_USTRINGPARAM( "TableCount" ) ) ) )
291 mrExport
.AddAttribute(
292 XML_NAMESPACE_META
, XML_TABLE_COUNT
, aValue
);
293 else if ( aDocStatistic
[nInd
].Name
.equals( ::rtl::OUString(
294 RTL_CONSTASCII_USTRINGPARAM( "ObjectCount" ) ) ) )
295 mrExport
.AddAttribute(
296 XML_NAMESPACE_META
, XML_OBJECT_COUNT
, aValue
);
297 else if ( aDocStatistic
[nInd
].Name
.equals( ::rtl::OUString(
298 RTL_CONSTASCII_USTRINGPARAM( "ImageCount" ) ) ) )
299 mrExport
.AddAttribute(
300 XML_NAMESPACE_META
, XML_IMAGE_COUNT
, aValue
);
301 else if ( aDocStatistic
[nInd
].Name
.equals( ::rtl::OUString(
302 RTL_CONSTASCII_USTRINGPARAM( "PageCount" ) ) ) )
303 mrExport
.AddAttribute(
304 XML_NAMESPACE_META
, XML_PAGE_COUNT
, aValue
);
305 else if ( aDocStatistic
[nInd
].Name
.equals( ::rtl::OUString(
306 RTL_CONSTASCII_USTRINGPARAM( "ParagraphCount" ) ) ) )
307 mrExport
.AddAttribute(
308 XML_NAMESPACE_META
, XML_PARAGRAPH_COUNT
, aValue
);
309 else if ( aDocStatistic
[nInd
].Name
.equals( ::rtl::OUString(
310 RTL_CONSTASCII_USTRINGPARAM( "WordCount" ) ) ) )
311 mrExport
.AddAttribute(
312 XML_NAMESPACE_META
, XML_WORD_COUNT
, aValue
);
313 else if ( aDocStatistic
[nInd
].Name
.equals( ::rtl::OUString(
314 RTL_CONSTASCII_USTRINGPARAM( "CharacterCount" ) ) ) )
315 mrExport
.AddAttribute(
316 XML_NAMESPACE_META
, XML_CHARACTER_COUNT
, aValue
);
317 else if ( aDocStatistic
[nInd
].Name
.equals( ::rtl::OUString(
318 RTL_CONSTASCII_USTRINGPARAM( "CellCount" ) ) ) )
319 mrExport
.AddAttribute(
320 XML_NAMESPACE_META
, XML_CELL_COUNT
, aValue
);
323 DBG_ASSERT( sal_False
, "Unknown statistic value!\n" );
327 SvXMLElementExport
aElem( mrExport
,
328 XML_NAMESPACE_META
, XML_DOCUMENT_STATISTIC
, sal_True
, sal_True
);
332 //-------------------------------------------------------------------------
334 static const char *s_xmlns
= "xmlns";
335 static const char *s_xmlns2
= "xmlns:";
336 static const char *s_meta
= "meta:";
337 static const char *s_href
= "xlink:href";
339 SvXMLMetaExport::SvXMLMetaExport(
341 const uno::Reference
<document::XDocumentProperties
>& i_rDocProps
) :
343 mxDocProps( i_rDocProps
),
347 DBG_ASSERT( mxDocProps
.is(), "no document properties" );
350 SvXMLMetaExport::~SvXMLMetaExport()
354 void SvXMLMetaExport::Export()
356 // exportDom(xDOM, mrExport); // this would not work (root node, namespaces)
357 uno::Reference
< xml::sax::XSAXSerializable
> xSAXable(mxDocProps
,
360 ::comphelper::SequenceAsVector
< beans::StringPair
> namespaces
;
361 const SvXMLNamespaceMap
& rNsMap(mrExport
.GetNamespaceMap());
362 for (sal_uInt16 key
= rNsMap
.GetFirstKey();
363 key
!= USHRT_MAX
; key
= rNsMap
.GetNextKey(key
)) {
364 beans::StringPair ns
;
365 const ::rtl::OUString attrname
= rNsMap
.GetAttrNameByKey(key
);
366 if (attrname
.matchAsciiL(s_xmlns2
, strlen(s_xmlns2
))) {
367 ns
.First
= attrname
.copy(strlen(s_xmlns2
));
368 } else if (attrname
.equalsAsciiL(s_xmlns
, strlen(s_xmlns
))) {
369 // default initialized empty string
371 DBG_ERROR("namespace attribute not starting with xmlns unexpected");
373 ns
.Second
= rNsMap
.GetNameByKey(key
);
374 namespaces
.push_back(ns
);
376 xSAXable
->serialize(this, namespaces
.getAsConstList());
379 SvXMLElementExport
aElem( mrExport
, XML_NAMESPACE_OFFICE
, XML_META
,
380 sal_True
, sal_True
);
381 // fall back to using public interface of XDocumentProperties
386 // ::com::sun::star::xml::sax::XDocumentHandler:
388 SvXMLMetaExport::startDocument()
389 throw (uno::RuntimeException
, xml::sax::SAXException
)
391 // ignore: has already been done by SvXMLExport::exportDoc
392 DBG_ASSERT( m_level
== 0, "SvXMLMetaExport: level error" );
396 SvXMLMetaExport::endDocument()
397 throw (uno::RuntimeException
, xml::sax::SAXException
)
399 // ignore: will be done by SvXMLExport::exportDoc
400 DBG_ASSERT( m_level
== 0, "SvXMLMetaExport: level error" );
403 // unfortunately, this method contains far too much ugly namespace mangling.
405 SvXMLMetaExport::startElement(const ::rtl::OUString
& i_rName
,
406 const uno::Reference
< xml::sax::XAttributeList
> & i_xAttribs
)
407 throw (uno::RuntimeException
, xml::sax::SAXException
)
411 // namepace decls: default ones have been written at the root element
412 // non-default ones must be preserved here
413 const sal_Int16 nCount
= i_xAttribs
->getLength();
414 for (sal_Int16 i
= 0; i
< nCount
; ++i
) {
415 const ::rtl::OUString
name(i_xAttribs
->getNameByIndex(i
));
416 if (name
.matchAsciiL(s_xmlns
, strlen(s_xmlns
))) {
418 const SvXMLNamespaceMap
& rNsMap(mrExport
.GetNamespaceMap());
419 for (sal_uInt16 key
= rNsMap
.GetFirstKey();
420 key
!= USHRT_MAX
; key
= rNsMap
.GetNextKey(key
)) {
421 if (name
.equals(rNsMap
.GetAttrNameByKey(key
))) {
427 m_preservedNSs
.push_back(beans::StringPair(name
,
428 i_xAttribs
->getValueByIndex(i
)));
432 // ignore the root: it has been written already
438 // attach preserved namespace decls from root node here
439 for (std::vector
<beans::StringPair
>::const_iterator iter
=
440 m_preservedNSs
.begin(); iter
!= m_preservedNSs
.end(); ++iter
) {
441 const ::rtl::OUString
ns(iter
->First
);
443 // but only if it is not already there
444 const sal_Int16 nCount
= i_xAttribs
->getLength();
445 for (sal_Int16 i
= 0; i
< nCount
; ++i
) {
446 const ::rtl::OUString
name(i_xAttribs
->getNameByIndex(i
));
447 if (ns
.equals(name
)) {
453 mrExport
.AddAttribute(ns
, iter
->Second
);
458 // attach the attributes
459 if (i_rName
.matchAsciiL(s_meta
, strlen(s_meta
))) {
460 // special handling for all elements that may have
461 // xlink:href attributes; these must be made relative
462 const sal_Int16 nLength
= i_xAttribs
->getLength();
463 for (sal_Int16 i
= 0; i
< nLength
; ++i
) {
464 const ::rtl::OUString
name (i_xAttribs
->getNameByIndex (i
));
465 ::rtl::OUString
value(i_xAttribs
->getValueByIndex(i
));
466 if (name
.matchAsciiL(s_href
, strlen(s_href
))) {
467 value
= mrExport
.GetRelativeReference(value
);
469 mrExport
.AddAttribute(name
, value
);
472 const sal_Int16 nLength
= i_xAttribs
->getLength();
473 for (sal_Int16 i
= 0; i
< nLength
; ++i
) {
474 const ::rtl::OUString
name (i_xAttribs
->getNameByIndex(i
));
475 const ::rtl::OUString
value (i_xAttribs
->getValueByIndex(i
));
476 mrExport
.AddAttribute(name
, value
);
480 // finally, start the element
481 mrExport
.StartElement(i_rName
, sal_True
); //FIXME:whitespace?
486 SvXMLMetaExport::endElement(const ::rtl::OUString
& i_rName
)
487 throw (uno::RuntimeException
, xml::sax::SAXException
)
491 // ignore the root; see startElement
494 DBG_ASSERT( m_level
>= 0, "SvXMLMetaExport: level error" );
495 mrExport
.EndElement(i_rName
, sal_False
);
499 SvXMLMetaExport::characters(const ::rtl::OUString
& i_rChars
)
500 throw (uno::RuntimeException
, xml::sax::SAXException
)
502 mrExport
.Characters(i_rChars
);
506 SvXMLMetaExport::ignorableWhitespace(const ::rtl::OUString
& /*i_rWhitespaces*/)
507 throw (uno::RuntimeException
, xml::sax::SAXException
)
509 mrExport
.IgnorableWhitespace(/*i_rWhitespaces*/);
513 SvXMLMetaExport::processingInstruction(const ::rtl::OUString
& i_rTarget
,
514 const ::rtl::OUString
& i_rData
)
515 throw (uno::RuntimeException
, xml::sax::SAXException
)
517 // ignore; the exporter cannot handle these
523 SvXMLMetaExport::setDocumentLocator(const uno::Reference
<xml::sax::XLocator
>&)
524 throw (uno::RuntimeException
, xml::sax::SAXException
)
526 // nothing to do here, move along...