bump product version to 5.0.4.1
[LibreOffice.git] / xmloff / source / meta / xmlmetae.cxx
bloba9bb477d6ac691818abdea8b428fece726f746f8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/debug.hxx>
21 #include <i18nlangtag/languagetag.hxx>
22 #include <rtl/ustrbuf.hxx>
24 #include <xmloff/xmlmetae.hxx>
25 #include <xmloff/xmlexp.hxx>
26 #include <xmloff/nmspmap.hxx>
27 #include <xmloff/xmlnmspe.hxx>
29 #include <com/sun/star/beans/XPropertyAccess.hpp>
30 #include <com/sun/star/beans/StringPair.hpp>
31 #include <com/sun/star/util/Duration.hpp>
32 #include <com/sun/star/xml/dom/XDocument.hpp>
33 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
35 #include <sax/tools/converter.hxx>
37 #include <comphelper/sequence.hxx>
38 #include <unotools/docinfohelper.hxx>
40 #include <string.h>
42 using namespace com::sun::star;
43 using namespace ::xmloff::token;
45 static void lcl_AddTwoDigits( OUStringBuffer& rStr, sal_Int32 nVal )
47 if ( nVal < 10 )
48 rStr.append( '0' );
49 rStr.append( nVal );
52 OUString
53 SvXMLMetaExport::GetISODateTimeString( const util::DateTime& rDateTime )
55 // return ISO date string "YYYY-MM-DDThh:mm:ss"
57 OUStringBuffer sTmp;
58 sTmp.append( (sal_Int32) rDateTime.Year );
59 sTmp.append( '-' );
60 lcl_AddTwoDigits( sTmp, rDateTime.Month );
61 sTmp.append( '-' );
62 lcl_AddTwoDigits( sTmp, rDateTime.Day );
63 sTmp.append( 'T' );
64 lcl_AddTwoDigits( sTmp, rDateTime.Hours );
65 sTmp.append( ':' );
66 lcl_AddTwoDigits( sTmp, rDateTime.Minutes );
67 sTmp.append( ':' );
68 lcl_AddTwoDigits( sTmp, rDateTime.Seconds );
70 return sTmp.makeStringAndClear();
73 void SvXMLMetaExport::SimpleStringElement( const OUString& rText,
74 sal_uInt16 nNamespace, enum XMLTokenEnum eElementName )
76 if ( !rText.isEmpty() ) {
77 SvXMLElementExport aElem( mrExport, nNamespace, eElementName,
78 true, false );
79 mrExport.Characters( rText );
83 void SvXMLMetaExport::SimpleDateTimeElement( const util::DateTime & rDate,
84 sal_uInt16 nNamespace, enum XMLTokenEnum eElementName )
86 if (rDate.Month != 0) { // invalid dates are 0-0-0
87 OUString sValue = GetISODateTimeString( rDate );
88 if ( !sValue.isEmpty() ) {
89 SvXMLElementExport aElem( mrExport, nNamespace, eElementName,
90 true, false );
91 mrExport.Characters( sValue );
96 void SvXMLMetaExport::_MExport()
98 // generator
100 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_GENERATOR,
101 true, true );
102 mrExport.Characters( ::utl::DocInfoHelper::GetGeneratorString() );
105 // document title
106 SimpleStringElement ( mxDocProps->getTitle(),
107 XML_NAMESPACE_DC, XML_TITLE );
109 // description
110 SimpleStringElement ( mxDocProps->getDescription(),
111 XML_NAMESPACE_DC, XML_DESCRIPTION );
113 // subject
114 SimpleStringElement ( mxDocProps->getSubject(),
115 XML_NAMESPACE_DC, XML_SUBJECT );
117 // created...
118 SimpleStringElement ( mxDocProps->getAuthor(),
119 XML_NAMESPACE_META, XML_INITIAL_CREATOR );
120 SimpleDateTimeElement( mxDocProps->getCreationDate(),
121 XML_NAMESPACE_META, XML_CREATION_DATE );
123 // modified...
124 SimpleStringElement ( mxDocProps->getModifiedBy(),
125 XML_NAMESPACE_DC, XML_CREATOR );
126 SimpleDateTimeElement( mxDocProps->getModificationDate(),
127 XML_NAMESPACE_DC, XML_DATE );
129 // printed...
130 SimpleStringElement ( mxDocProps->getPrintedBy(),
131 XML_NAMESPACE_META, XML_PRINTED_BY );
132 SimpleDateTimeElement( mxDocProps->getPrintDate(),
133 XML_NAMESPACE_META, XML_PRINT_DATE );
135 // keywords
136 const uno::Sequence< OUString > keywords = mxDocProps->getKeywords();
137 for (sal_Int32 i = 0; i < keywords.getLength(); ++i) {
138 SvXMLElementExport aKwElem( mrExport, XML_NAMESPACE_META, XML_KEYWORD,
139 true, false );
140 mrExport.Characters( keywords[i] );
143 // document language
145 OUString sValue = LanguageTag( mxDocProps->getLanguage()).getBcp47( false);
146 if (!sValue.isEmpty()) {
147 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DC, XML_LANGUAGE,
148 true, false );
149 mrExport.Characters( sValue );
153 // editing cycles
155 SvXMLElementExport aElem( mrExport,
156 XML_NAMESPACE_META, XML_EDITING_CYCLES,
157 true, false );
158 mrExport.Characters( OUString::number(
159 mxDocProps->getEditingCycles() ) );
162 // editing duration
163 // property is a int32 (seconds)
165 sal_Int32 secs = mxDocProps->getEditingDuration();
166 SvXMLElementExport aElem( mrExport,
167 XML_NAMESPACE_META, XML_EDITING_DURATION,
168 true, false );
169 OUStringBuffer buf;
170 ::sax::Converter::convertDuration(buf, util::Duration(
171 false, 0, 0, 0, secs/3600, (secs%3600)/60, secs%60, 0));
172 mrExport.Characters(buf.makeStringAndClear());
175 // default target
176 const OUString sDefTarget = mxDocProps->getDefaultTarget();
177 if ( !sDefTarget.isEmpty() )
179 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME,
180 sDefTarget );
182 //! define strings for xlink:show values
183 const XMLTokenEnum eShow = sDefTarget == "_blank" ? XML_NEW : XML_REPLACE;
184 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, eShow );
186 SvXMLElementExport aElem( mrExport,
187 XML_NAMESPACE_META,XML_HYPERLINK_BEHAVIOUR,
188 true, false );
191 // auto-reload
192 const OUString sReloadURL = mxDocProps->getAutoloadURL();
193 const sal_Int32 sReloadDelay = mxDocProps->getAutoloadSecs();
194 if (sReloadDelay != 0 || !sReloadURL.isEmpty())
196 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF,
197 mrExport.GetRelativeReference( sReloadURL ) );
199 OUStringBuffer buf;
200 ::sax::Converter::convertDuration(buf, util::Duration(false, 0, 0, 0,
201 sReloadDelay/3600, (sReloadDelay%3600)/60, sReloadDelay%60, 0));
202 mrExport.AddAttribute( XML_NAMESPACE_META, XML_DELAY,
203 buf.makeStringAndClear());
205 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_AUTO_RELOAD,
206 true, false );
209 // template
210 const OUString sTplPath = mxDocProps->getTemplateURL();
211 if ( !sTplPath.isEmpty() )
213 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
214 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONREQUEST );
216 // template URL
217 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF,
218 mrExport.GetRelativeReference(sTplPath) );
220 // template name
221 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TITLE,
222 mxDocProps->getTemplateName() );
224 // template date
225 mrExport.AddAttribute( XML_NAMESPACE_META, XML_DATE,
226 GetISODateTimeString( mxDocProps->getTemplateDate() ) );
228 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_TEMPLATE,
229 true, false );
232 // user defined fields
233 uno::Reference< beans::XPropertyAccess > xUserDefined(
234 mxDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
235 const uno::Sequence< beans::PropertyValue > props =
236 xUserDefined->getPropertyValues();
237 for (sal_Int32 i = 0; i < props.getLength(); ++i) {
238 OUStringBuffer sValueBuffer;
239 OUStringBuffer sType;
240 if (!::sax::Converter::convertAny(sValueBuffer, sType, props[i].Value))
242 continue;
244 mrExport.AddAttribute( XML_NAMESPACE_META, XML_NAME, props[i].Name );
245 mrExport.AddAttribute( XML_NAMESPACE_META, XML_VALUE_TYPE,
246 sType.makeStringAndClear() );
247 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META,
248 XML_USER_DEFINED, true, false );
249 mrExport.Characters( sValueBuffer.makeStringAndClear() );
252 const uno::Sequence< beans::NamedValue > aDocStatistic =
253 mxDocProps->getDocumentStatistics();
254 // write document statistic if there is any provided
255 if ( aDocStatistic.getLength() )
257 for ( sal_Int32 nInd = 0; nInd < aDocStatistic.getLength(); nInd++ )
259 sal_Int32 nValue = 0;
260 if ( aDocStatistic[nInd].Value >>= nValue )
262 OUString aValue = OUString::number( nValue );
263 if ( aDocStatistic[nInd].Name == "TableCount" )
264 mrExport.AddAttribute(
265 XML_NAMESPACE_META, XML_TABLE_COUNT, aValue );
266 else if ( aDocStatistic[nInd].Name == "ObjectCount" )
267 mrExport.AddAttribute(
268 XML_NAMESPACE_META, XML_OBJECT_COUNT, aValue );
269 else if ( aDocStatistic[nInd].Name == "ImageCount" )
270 mrExport.AddAttribute(
271 XML_NAMESPACE_META, XML_IMAGE_COUNT, aValue );
272 else if ( aDocStatistic[nInd].Name == "PageCount" )
273 mrExport.AddAttribute(
274 XML_NAMESPACE_META, XML_PAGE_COUNT, aValue );
275 else if ( aDocStatistic[nInd].Name == "ParagraphCount" )
276 mrExport.AddAttribute(
277 XML_NAMESPACE_META, XML_PARAGRAPH_COUNT, aValue );
278 else if ( aDocStatistic[nInd].Name == "WordCount" )
279 mrExport.AddAttribute(
280 XML_NAMESPACE_META, XML_WORD_COUNT, aValue );
281 else if ( aDocStatistic[nInd].Name == "CharacterCount" )
282 mrExport.AddAttribute(
283 XML_NAMESPACE_META, XML_CHARACTER_COUNT, aValue );
284 else if ( aDocStatistic[nInd].Name == "CellCount" )
285 mrExport.AddAttribute(
286 XML_NAMESPACE_META, XML_CELL_COUNT, aValue );
287 else
289 SAL_WARN("xmloff", "Unknown statistic value!");
293 SvXMLElementExport aElem( mrExport,
294 XML_NAMESPACE_META, XML_DOCUMENT_STATISTIC, true, true );
298 static const char *s_xmlns = "xmlns";
299 static const char *s_xmlns2 = "xmlns:";
300 static const char *s_meta = "meta:";
301 static const char *s_href = "xlink:href";
303 SvXMLMetaExport::SvXMLMetaExport(
304 SvXMLExport& i_rExp,
305 const uno::Reference<document::XDocumentProperties>& i_rDocProps ) :
306 mrExport( i_rExp ),
307 mxDocProps( i_rDocProps ),
308 m_level( 0 ),
309 m_preservedNSs()
311 assert(mxDocProps.is());
314 SvXMLMetaExport::~SvXMLMetaExport()
318 void SvXMLMetaExport::Export()
320 uno::Reference< xml::sax::XSAXSerializable> xSAXable(mxDocProps,
321 uno::UNO_QUERY);
322 if (xSAXable.is()) {
323 ::std::vector< beans::StringPair > namespaces;
324 const SvXMLNamespaceMap & rNsMap(mrExport.GetNamespaceMap());
325 for (sal_uInt16 key = rNsMap.GetFirstKey();
326 key != USHRT_MAX; key = rNsMap.GetNextKey(key)) {
327 beans::StringPair ns;
328 const OUString attrname = rNsMap.GetAttrNameByKey(key);
329 if (attrname.matchAsciiL(s_xmlns2, strlen(s_xmlns2))) {
330 ns.First = attrname.copy(strlen(s_xmlns2));
331 } else if (attrname.equalsAsciiL(s_xmlns, strlen(s_xmlns))) {
332 // default initialized empty string
333 } else {
334 assert(!"namespace attribute not starting with xmlns unexpected");
336 ns.Second = rNsMap.GetNameByKey(key);
337 namespaces.push_back(ns);
339 xSAXable->serialize(this, comphelper::containerToSequence(namespaces));
340 } else {
341 // office:meta
342 SvXMLElementExport aElem( mrExport, XML_NAMESPACE_OFFICE, XML_META,
343 true, true );
344 // fall back to using public interface of XDocumentProperties
345 _MExport();
349 // ::com::sun::star::xml::sax::XDocumentHandler:
350 void SAL_CALL
351 SvXMLMetaExport::startDocument()
352 throw (uno::RuntimeException, xml::sax::SAXException, std::exception)
354 // ignore: has already been done by SvXMLExport::exportDoc
355 assert(m_level == 0 && "SvXMLMetaExport: level error");
358 void SAL_CALL
359 SvXMLMetaExport::endDocument()
360 throw (uno::RuntimeException, xml::sax::SAXException, std::exception)
362 // ignore: will be done by SvXMLExport::exportDoc
363 assert(m_level == 0 && "SvXMLMetaExport: level error");
366 // unfortunately, this method contains far too much ugly namespace mangling.
367 void SAL_CALL
368 SvXMLMetaExport::startElement(const OUString & i_rName,
369 const uno::Reference< xml::sax::XAttributeList > & i_xAttribs)
370 throw (uno::RuntimeException, xml::sax::SAXException, std::exception)
373 if (m_level == 0) {
374 // namespace decls: default ones have been written at the root element
375 // non-default ones must be preserved here
376 const sal_Int16 nCount = i_xAttribs->getLength();
377 for (sal_Int16 i = 0; i < nCount; ++i) {
378 const OUString name(i_xAttribs->getNameByIndex(i));
379 if (name.matchAsciiL(s_xmlns, strlen(s_xmlns))) {
380 bool found(false);
381 const SvXMLNamespaceMap & rNsMap(mrExport.GetNamespaceMap());
382 for (sal_uInt16 key = rNsMap.GetFirstKey();
383 key != USHRT_MAX; key = rNsMap.GetNextKey(key)) {
384 if (name.equals(rNsMap.GetAttrNameByKey(key))) {
385 found = true;
386 break;
389 if (!found) {
390 m_preservedNSs.push_back(beans::StringPair(name,
391 i_xAttribs->getValueByIndex(i)));
395 // ignore the root: it has been written already
396 ++m_level;
397 return;
400 if (m_level == 1) {
401 // attach preserved namespace decls from root node here
402 for (std::vector<beans::StringPair>::const_iterator iter =
403 m_preservedNSs.begin(); iter != m_preservedNSs.end(); ++iter) {
404 const OUString ns(iter->First);
405 bool found(false);
406 // but only if it is not already there
407 const sal_Int16 nCount = i_xAttribs->getLength();
408 for (sal_Int16 i = 0; i < nCount; ++i) {
409 const OUString name(i_xAttribs->getNameByIndex(i));
410 if (ns.equals(name)) {
411 found = true;
412 break;
415 if (!found) {
416 mrExport.AddAttribute(ns, iter->Second);
421 // attach the attributes
422 if (i_rName.matchAsciiL(s_meta, strlen(s_meta))) {
423 // special handling for all elements that may have
424 // xlink:href attributes; these must be made relative
425 const sal_Int16 nLength = i_xAttribs->getLength();
426 for (sal_Int16 i = 0; i < nLength; ++i) {
427 const OUString name (i_xAttribs->getNameByIndex (i));
428 OUString value(i_xAttribs->getValueByIndex(i));
429 if (name.matchAsciiL(s_href, strlen(s_href))) {
430 value = mrExport.GetRelativeReference(value);
432 mrExport.AddAttribute(name, value);
434 } else {
435 const sal_Int16 nLength = i_xAttribs->getLength();
436 for (sal_Int16 i = 0; i < nLength; ++i) {
437 const OUString name (i_xAttribs->getNameByIndex(i));
438 const OUString value (i_xAttribs->getValueByIndex(i));
439 mrExport.AddAttribute(name, value);
443 // finally, start the element
444 // #i107240# no whitespace here, because the DOM may already contain
445 // whitespace, which is not cleared when loading and thus accumulates.
446 mrExport.StartElement(i_rName, m_level <= 1);
447 ++m_level;
450 void SAL_CALL
451 SvXMLMetaExport::endElement(const OUString & i_rName)
452 throw (uno::RuntimeException, xml::sax::SAXException, std::exception)
454 --m_level;
455 if (m_level == 0) {
456 // ignore the root; see startElement
457 return;
459 assert(m_level >= 0 && "SvXMLMetaExport: level error");
460 mrExport.EndElement(i_rName, false);
463 void SAL_CALL
464 SvXMLMetaExport::characters(const OUString & i_rChars)
465 throw (uno::RuntimeException, xml::sax::SAXException, std::exception)
467 mrExport.Characters(i_rChars);
470 void SAL_CALL
471 SvXMLMetaExport::ignorableWhitespace(const OUString & /*i_rWhitespaces*/)
472 throw (uno::RuntimeException, xml::sax::SAXException, std::exception)
474 mrExport.IgnorableWhitespace(/*i_rWhitespaces*/);
477 void SAL_CALL
478 SvXMLMetaExport::processingInstruction(const OUString & i_rTarget,
479 const OUString & i_rData)
480 throw (uno::RuntimeException, xml::sax::SAXException, std::exception)
482 // ignore; the exporter cannot handle these
483 (void) i_rTarget;
484 (void) i_rData;
487 void SAL_CALL
488 SvXMLMetaExport::setDocumentLocator(const uno::Reference<xml::sax::XLocator>&)
489 throw (uno::RuntimeException, xml::sax::SAXException, std::exception)
491 // nothing to do here, move along...
494 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */