xref: /aoo41x/main/xmloff/source/meta/xmlmetae.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_xmloff.hxx"
30 
31 #include <tools/debug.hxx>
32 #include <tools/inetdef.hxx>
33 #include <i18npool/mslangid.hxx>
34 #include <tools/urlobj.hxx>
35 #include <tools/time.hxx>
36 #include <rtl/ustrbuf.hxx>
37 
38 #include <xmloff/xmlmetae.hxx>
39 #include <xmloff/xmlexp.hxx>
40 #include <xmloff/xmluconv.hxx>
41 #include <xmloff/nmspmap.hxx>
42 #include "xmloff/xmlnmspe.hxx"
43 
44 #include <com/sun/star/beans/XPropertyAccess.hpp>
45 #include <com/sun/star/beans/StringPair.hpp>
46 #include <com/sun/star/xml/dom/XDocument.hpp>
47 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
48 
49 #include <comphelper/sequenceasvector.hxx>
50 #include <unotools/docinfohelper.hxx>
51 
52 #include <string.h>
53 
54 
55 using namespace com::sun::star;
56 using namespace ::xmloff::token;
57 
58 
59 //-------------------------------------------------------------------------
60 
61 void lcl_AddTwoDigits( rtl::OUStringBuffer& rStr, sal_Int32 nVal )
62 {
63     if ( nVal < 10 )
64         rStr.append( sal_Unicode('0') );
65     rStr.append( nVal );
66 }
67 
68 rtl::OUString
69 SvXMLMetaExport::GetISODateTimeString( const util::DateTime& rDateTime )
70 {
71     //  return ISO date string "YYYY-MM-DDThh:mm:ss"
72 
73     rtl::OUStringBuffer sTmp;
74     sTmp.append( (sal_Int32) rDateTime.Year );
75     sTmp.append( sal_Unicode('-') );
76     lcl_AddTwoDigits( sTmp, rDateTime.Month );
77     sTmp.append( sal_Unicode('-') );
78     lcl_AddTwoDigits( sTmp, rDateTime.Day );
79     sTmp.append( sal_Unicode('T') );
80     lcl_AddTwoDigits( sTmp, rDateTime.Hours );
81     sTmp.append( sal_Unicode(':') );
82     lcl_AddTwoDigits( sTmp, rDateTime.Minutes );
83     sTmp.append( sal_Unicode(':') );
84     lcl_AddTwoDigits( sTmp, rDateTime.Seconds );
85 
86     return sTmp.makeStringAndClear();
87 }
88 
89 //-------------------------------------------------------------------------
90 
91 void SvXMLMetaExport::SimpleStringElement( const rtl::OUString& rText,
92         sal_uInt16 nNamespace, enum XMLTokenEnum eElementName )
93 {
94     if ( rText.getLength() ) {
95         SvXMLElementExport aElem( mrExport, nNamespace, eElementName,
96                                   sal_True, sal_False );
97         mrExport.Characters( rText );
98     }
99 }
100 
101 void SvXMLMetaExport::SimpleDateTimeElement( const util::DateTime & rDate,
102         sal_uInt16 nNamespace, enum XMLTokenEnum eElementName )
103 {
104     if (rDate.Month != 0) { // invalid dates are 0-0-0
105         rtl::OUString sValue = GetISODateTimeString( rDate );
106         if ( sValue.getLength() ) {
107             SvXMLElementExport aElem( mrExport, nNamespace, eElementName,
108                                       sal_True, sal_False );
109             mrExport.Characters( sValue );
110         }
111     }
112 }
113 
114 void SvXMLMetaExport::_MExport()
115 {
116     //  generator
117     {
118         SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_GENERATOR,
119                                   sal_True, sal_True );
120         mrExport.Characters( ::utl::DocInfoHelper::GetGeneratorString() );
121     }
122 
123     //  document title
124     SimpleStringElement  ( mxDocProps->getTitle(),
125                            XML_NAMESPACE_DC, XML_TITLE );
126 
127     //  description
128     SimpleStringElement  ( mxDocProps->getDescription(),
129                            XML_NAMESPACE_DC, XML_DESCRIPTION );
130 
131     //  subject
132     SimpleStringElement  ( mxDocProps->getSubject(),
133                            XML_NAMESPACE_DC, XML_SUBJECT );
134 
135     //  created...
136     SimpleStringElement  ( mxDocProps->getAuthor(),
137                            XML_NAMESPACE_META, XML_INITIAL_CREATOR );
138     SimpleDateTimeElement( mxDocProps->getCreationDate(),
139                            XML_NAMESPACE_META, XML_CREATION_DATE );
140 
141     //  modified...
142     SimpleStringElement  ( mxDocProps->getModifiedBy(),
143                            XML_NAMESPACE_DC, XML_CREATOR );
144     SimpleDateTimeElement( mxDocProps->getModificationDate(),
145                            XML_NAMESPACE_DC, XML_DATE );
146 
147     //  printed...
148     SimpleStringElement  ( mxDocProps->getPrintedBy(),
149                            XML_NAMESPACE_META, XML_PRINTED_BY );
150     SimpleDateTimeElement( mxDocProps->getPrintDate(),
151                            XML_NAMESPACE_META, XML_PRINT_DATE );
152 
153     //  keywords
154     const uno::Sequence< ::rtl::OUString > keywords = mxDocProps->getKeywords();
155     for (sal_Int32 i = 0; i < keywords.getLength(); ++i) {
156         SvXMLElementExport aKwElem( mrExport, XML_NAMESPACE_META, XML_KEYWORD,
157                                     sal_True, sal_False );
158         mrExport.Characters( keywords[i] );
159     }
160 
161     //  document language
162     {
163         const lang::Locale aLocale = mxDocProps->getLanguage();
164         ::rtl::OUString sValue = aLocale.Language;
165         if (sValue.getLength()) {
166             if ( aLocale.Country.getLength() )
167             {
168                 sValue += rtl::OUString::valueOf((sal_Unicode)'-');
169                 sValue += aLocale.Country;
170             }
171             SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DC, XML_LANGUAGE,
172                                       sal_True, sal_False );
173             mrExport.Characters( sValue );
174         }
175     }
176 
177     //  editing cycles
178     {
179         SvXMLElementExport aElem( mrExport,
180                                   XML_NAMESPACE_META, XML_EDITING_CYCLES,
181                                   sal_True, sal_False );
182         mrExport.Characters( ::rtl::OUString::valueOf(
183             static_cast<sal_Int32>(mxDocProps->getEditingCycles()) ) );
184     }
185 
186     //  editing duration
187     //  property is a int32 (seconds)
188     {
189         sal_Int32 secs = mxDocProps->getEditingDuration();
190         SvXMLElementExport aElem( mrExport,
191                                   XML_NAMESPACE_META, XML_EDITING_DURATION,
192                                   sal_True, sal_False );
193         mrExport.Characters( SvXMLUnitConverter::convertTimeDuration(
194             Time(secs/3600, (secs%3600)/60, secs%60)) );
195     }
196 
197     //  default target
198     const ::rtl::OUString sDefTarget = mxDocProps->getDefaultTarget();
199     if ( sDefTarget.getLength() )
200     {
201         mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME,
202                                sDefTarget );
203 
204         //! define strings for xlink:show values
205         const XMLTokenEnum eShow =
206             sDefTarget.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("_blank"))
207                 ? XML_NEW : XML_REPLACE;
208         mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_SHOW, eShow );
209 
210         SvXMLElementExport aElem( mrExport,
211                                   XML_NAMESPACE_META,XML_HYPERLINK_BEHAVIOUR,
212                                   sal_True, sal_False );
213     }
214 
215     //  auto-reload
216     const ::rtl::OUString sReloadURL = mxDocProps->getAutoloadURL();
217     const sal_Int32 sReloadDelay = mxDocProps->getAutoloadSecs();
218     if (sReloadDelay != 0 || sReloadURL.getLength() != 0)
219     {
220         mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF,
221                               mrExport.GetRelativeReference( sReloadURL ) );
222 
223         mrExport.AddAttribute( XML_NAMESPACE_META, XML_DELAY,
224             SvXMLUnitConverter::convertTimeDuration(
225                 Time(sReloadDelay/3600, (sReloadDelay%3600)/60,
226                     sReloadDelay%60 )) );
227 
228         SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_AUTO_RELOAD,
229                                   sal_True, sal_False );
230     }
231 
232     //  template
233     const rtl::OUString sTplPath = mxDocProps->getTemplateURL();
234     if ( sTplPath.getLength() )
235     {
236         mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
237         mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONREQUEST );
238 
239         //  template URL
240         mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF,
241                               mrExport.GetRelativeReference(sTplPath) );
242 
243         //  template name
244         mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TITLE,
245                               mxDocProps->getTemplateName() );
246 
247         //  template date
248         mrExport.AddAttribute( XML_NAMESPACE_META, XML_DATE,
249                 GetISODateTimeString( mxDocProps->getTemplateDate() ) );
250 
251         SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META, XML_TEMPLATE,
252                                   sal_True, sal_False );
253     }
254 
255     //  user defined fields
256     uno::Reference< beans::XPropertyAccess > xUserDefined(
257         mxDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
258     const uno::Sequence< beans::PropertyValue > props =
259         xUserDefined->getPropertyValues();
260     for (sal_Int32 i = 0; i < props.getLength(); ++i) {
261         ::rtl::OUStringBuffer sValueBuffer;
262         ::rtl::OUStringBuffer sType;
263         if (!SvXMLUnitConverter::convertAny(
264                 sValueBuffer, sType, props[i].Value)) {
265             continue;
266         }
267         mrExport.AddAttribute( XML_NAMESPACE_META, XML_NAME, props[i].Name );
268         mrExport.AddAttribute( XML_NAMESPACE_META, XML_VALUE_TYPE,
269                               sType.makeStringAndClear() );
270         SvXMLElementExport aElem( mrExport, XML_NAMESPACE_META,
271                                   XML_USER_DEFINED, sal_True, sal_False );
272         mrExport.Characters( sValueBuffer.makeStringAndClear() );
273     }
274 
275     const uno::Sequence< beans::NamedValue > aDocStatistic =
276             mxDocProps->getDocumentStatistics();
277 	// write document statistic if there is any provided
278 	if ( aDocStatistic.getLength() )
279 	{
280 		for ( sal_Int32 nInd = 0; nInd < aDocStatistic.getLength(); nInd++ )
281 		{
282 			sal_Int32 nValue = 0;
283 			if ( aDocStatistic[nInd].Value >>= nValue )
284 			{
285 				::rtl::OUString aValue = rtl::OUString::valueOf( nValue );
286 				if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
287                         RTL_CONSTASCII_USTRINGPARAM( "TableCount" ) ) ) )
288 					mrExport.AddAttribute(
289                         XML_NAMESPACE_META, XML_TABLE_COUNT, aValue );
290 				else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
291                         RTL_CONSTASCII_USTRINGPARAM( "ObjectCount" ) ) ) )
292 					mrExport.AddAttribute(
293                         XML_NAMESPACE_META, XML_OBJECT_COUNT, aValue );
294 				else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
295                         RTL_CONSTASCII_USTRINGPARAM( "ImageCount" ) ) ) )
296 					mrExport.AddAttribute(
297                         XML_NAMESPACE_META, XML_IMAGE_COUNT, aValue );
298 				else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
299                         RTL_CONSTASCII_USTRINGPARAM( "PageCount" ) ) ) )
300 					mrExport.AddAttribute(
301                         XML_NAMESPACE_META, XML_PAGE_COUNT, aValue );
302 				else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
303                         RTL_CONSTASCII_USTRINGPARAM( "ParagraphCount" ) ) ) )
304 					mrExport.AddAttribute(
305                         XML_NAMESPACE_META, XML_PARAGRAPH_COUNT, aValue );
306 				else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
307                         RTL_CONSTASCII_USTRINGPARAM( "WordCount" ) ) ) )
308 					mrExport.AddAttribute(
309                         XML_NAMESPACE_META, XML_WORD_COUNT, aValue );
310 				else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
311                         RTL_CONSTASCII_USTRINGPARAM( "CharacterCount" ) ) ) )
312 					mrExport.AddAttribute(
313                         XML_NAMESPACE_META, XML_CHARACTER_COUNT, aValue );
314 				else if ( aDocStatistic[nInd].Name.equals( ::rtl::OUString(
315                         RTL_CONSTASCII_USTRINGPARAM( "CellCount" ) ) ) )
316 					mrExport.AddAttribute(
317                         XML_NAMESPACE_META, XML_CELL_COUNT, aValue );
318 				else
319 				{
320 					DBG_ASSERT( sal_False, "Unknown statistic value!\n" );
321 				}
322 			}
323 		}
324 		SvXMLElementExport aElem( mrExport,
325             XML_NAMESPACE_META, XML_DOCUMENT_STATISTIC, sal_True, sal_True );
326 	}
327 }
328 
329 //-------------------------------------------------------------------------
330 
331 static const char *s_xmlns  = "xmlns";
332 static const char *s_xmlns2 = "xmlns:";
333 static const char *s_meta   = "meta:";
334 static const char *s_href   = "xlink:href";
335 
336 SvXMLMetaExport::SvXMLMetaExport(
337         SvXMLExport& i_rExp,
338         const uno::Reference<document::XDocumentProperties>& i_rDocProps ) :
339     mrExport( i_rExp ),
340 	mxDocProps( i_rDocProps ),
341     m_level( 0 ),
342     m_preservedNSs()
343 {
344     DBG_ASSERT( mxDocProps.is(), "no document properties" );
345 }
346 
347 SvXMLMetaExport::~SvXMLMetaExport()
348 {
349 }
350 
351 void SvXMLMetaExport::Export()
352 {
353 //    exportDom(xDOM, mrExport); // this would not work (root node, namespaces)
354     uno::Reference< xml::sax::XSAXSerializable> xSAXable(mxDocProps,
355         uno::UNO_QUERY);
356     if (xSAXable.is()) {
357         ::comphelper::SequenceAsVector< beans::StringPair > namespaces;
358         const SvXMLNamespaceMap & rNsMap(mrExport.GetNamespaceMap());
359         for (sal_uInt16 key = rNsMap.GetFirstKey();
360              key != USHRT_MAX; key = rNsMap.GetNextKey(key)) {
361             beans::StringPair ns;
362             const ::rtl::OUString attrname = rNsMap.GetAttrNameByKey(key);
363             if (attrname.matchAsciiL(s_xmlns2, strlen(s_xmlns2))) {
364                 ns.First  = attrname.copy(strlen(s_xmlns2));
365             } else if (attrname.equalsAsciiL(s_xmlns, strlen(s_xmlns))) {
366                 // default initialized empty string
367             } else {
368             DBG_ERROR("namespace attribute not starting with xmlns unexpected");
369             }
370             ns.Second = rNsMap.GetNameByKey(key);
371             namespaces.push_back(ns);
372         }
373         xSAXable->serialize(this, namespaces.getAsConstList());
374     } else {
375         // office:meta
376 		SvXMLElementExport aElem( mrExport, XML_NAMESPACE_OFFICE, XML_META,
377 								  sal_True, sal_True );
378         // fall back to using public interface of XDocumentProperties
379         _MExport();
380     }
381 }
382 
383 // ::com::sun::star::xml::sax::XDocumentHandler:
384 void SAL_CALL
385 SvXMLMetaExport::startDocument()
386     throw (uno::RuntimeException, xml::sax::SAXException)
387 {
388     // ignore: has already been done by SvXMLExport::exportDoc
389     DBG_ASSERT( m_level == 0, "SvXMLMetaExport: level error" );
390 }
391 
392 void SAL_CALL
393 SvXMLMetaExport::endDocument()
394     throw (uno::RuntimeException, xml::sax::SAXException)
395 {
396     // ignore: will be done by SvXMLExport::exportDoc
397     DBG_ASSERT( m_level == 0, "SvXMLMetaExport: level error" );
398 }
399 
400 // unfortunately, this method contains far too much ugly namespace mangling.
401 void SAL_CALL
402 SvXMLMetaExport::startElement(const ::rtl::OUString & i_rName,
403     const uno::Reference< xml::sax::XAttributeList > & i_xAttribs)
404     throw (uno::RuntimeException, xml::sax::SAXException)
405 {
406 
407     if (m_level == 0) {
408         // namepace decls: default ones have been written at the root element
409         // non-default ones must be preserved here
410         const sal_Int16 nCount = i_xAttribs->getLength();
411         for (sal_Int16 i = 0; i < nCount; ++i) {
412             const ::rtl::OUString name(i_xAttribs->getNameByIndex(i));
413             if (name.matchAsciiL(s_xmlns, strlen(s_xmlns))) {
414                 bool found(false);
415                 const SvXMLNamespaceMap & rNsMap(mrExport.GetNamespaceMap());
416                 for (sal_uInt16 key = rNsMap.GetFirstKey();
417                      key != USHRT_MAX; key = rNsMap.GetNextKey(key)) {
418                     if (name.equals(rNsMap.GetAttrNameByKey(key))) {
419                         found = true;
420                         break;
421                     }
422                 }
423                 if (!found) {
424                     m_preservedNSs.push_back(beans::StringPair(name,
425                         i_xAttribs->getValueByIndex(i)));
426                 }
427             }
428         }
429         // ignore the root: it has been written already
430         ++m_level;
431         return;
432     }
433 
434     if (m_level == 1) {
435         // attach preserved namespace decls from root node here
436         for (std::vector<beans::StringPair>::const_iterator iter =
437                 m_preservedNSs.begin(); iter != m_preservedNSs.end(); ++iter) {
438             const ::rtl::OUString ns(iter->First);
439             bool found(false);
440             // but only if it is not already there
441             const sal_Int16 nCount = i_xAttribs->getLength();
442             for (sal_Int16 i = 0; i < nCount; ++i) {
443                 const ::rtl::OUString name(i_xAttribs->getNameByIndex(i));
444                 if (ns.equals(name)) {
445                     found = true;
446                     break;
447                 }
448             }
449             if (!found) {
450                 mrExport.AddAttribute(ns, iter->Second);
451             }
452         }
453     }
454 
455     // attach the attributes
456     if (i_rName.matchAsciiL(s_meta, strlen(s_meta))) {
457         // special handling for all elements that may have
458         // xlink:href attributes; these must be made relative
459         const sal_Int16 nLength = i_xAttribs->getLength();
460         for (sal_Int16 i = 0; i < nLength; ++i) {
461             const ::rtl::OUString name (i_xAttribs->getNameByIndex (i));
462             ::rtl::OUString value(i_xAttribs->getValueByIndex(i));
463             if (name.matchAsciiL(s_href, strlen(s_href))) {
464                 value = mrExport.GetRelativeReference(value);
465             }
466             mrExport.AddAttribute(name, value);
467         }
468     } else {
469         const sal_Int16 nLength = i_xAttribs->getLength();
470         for (sal_Int16 i = 0; i < nLength; ++i) {
471             const ::rtl::OUString name  (i_xAttribs->getNameByIndex(i));
472             const ::rtl::OUString value (i_xAttribs->getValueByIndex(i));
473             mrExport.AddAttribute(name, value);
474         }
475     }
476 
477     // finally, start the element
478     // #i107240# no whitespace here, because the DOM may already contain
479     // whitespace, which is not cleared when loading and thus accumulates.
480     mrExport.StartElement(i_rName, (m_level > 1) ? sal_False : sal_True);
481     ++m_level;
482 }
483 
484 void SAL_CALL
485 SvXMLMetaExport::endElement(const ::rtl::OUString & i_rName)
486     throw (uno::RuntimeException, xml::sax::SAXException)
487 {
488     --m_level;
489     if (m_level == 0) {
490         // ignore the root; see startElement
491         return;
492     }
493     DBG_ASSERT( m_level >= 0, "SvXMLMetaExport: level error" );
494     mrExport.EndElement(i_rName, sal_False);
495 }
496 
497 void SAL_CALL
498 SvXMLMetaExport::characters(const ::rtl::OUString & i_rChars)
499     throw (uno::RuntimeException, xml::sax::SAXException)
500 {
501     mrExport.Characters(i_rChars);
502 }
503 
504 void SAL_CALL
505 SvXMLMetaExport::ignorableWhitespace(const ::rtl::OUString & /*i_rWhitespaces*/)
506     throw (uno::RuntimeException, xml::sax::SAXException)
507 {
508     mrExport.IgnorableWhitespace(/*i_rWhitespaces*/);
509 }
510 
511 void SAL_CALL
512 SvXMLMetaExport::processingInstruction(const ::rtl::OUString & i_rTarget,
513     const ::rtl::OUString & i_rData)
514     throw (uno::RuntimeException, xml::sax::SAXException)
515 {
516     // ignore; the exporter cannot handle these
517     (void) i_rTarget;
518     (void) i_rData;
519 }
520 
521 void SAL_CALL
522 SvXMLMetaExport::setDocumentLocator(const uno::Reference<xml::sax::XLocator>&)
523     throw (uno::RuntimeException, xml::sax::SAXException)
524 {
525     // nothing to do here, move along...
526 }
527 
528 
529