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