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