Bump version to 24.04.3.4
[LibreOffice.git] / oox / source / docprop / docprophandler.cxx
blob3f2d962c8f901d9c63612b0cb83c849e27fb5dbb
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 "docprophandler.hxx"
22 #include <com/sun/star/beans/PropertyAttribute.hpp>
23 #include <com/sun/star/beans/PropertyExistException.hpp>
24 #include <com/sun/star/lang/IllegalArgumentException.hpp>
25 #include <com/sun/star/xml/sax/SAXException.hpp>
26 #include <cppuhelper/exc_hlp.hxx>
27 #include <o3tl/string_view.hxx>
29 #include <o3tl/safeint.hxx>
30 #include <osl/time.h>
31 #include <osl/diagnose.h>
32 #include <sal/log.hxx>
33 #include <i18nlangtag/languagetag.hxx>
35 #include <vector>
36 #include <boost/algorithm/string.hpp>
38 #include <oox/helper/attributelist.hxx>
40 using namespace ::com::sun::star;
42 namespace oox::docprop {
44 OOXMLDocPropHandler::OOXMLDocPropHandler( const uno::Reference< uno::XComponentContext >& xContext,
45 const uno::Reference< document::XDocumentProperties >& rDocProp )
46 : m_xDocProp( rDocProp )
47 , m_nState( 0 )
48 , m_nBlock( 0 )
49 , m_nType( 0 )
50 , m_nInBlock( 0 )
51 , m_CustomStringPropertyState(NONE)
53 if ( !xContext.is() || !rDocProp.is() )
54 throw uno::RuntimeException();
57 OOXMLDocPropHandler::~OOXMLDocPropHandler()
61 void OOXMLDocPropHandler::InitNew()
63 m_nState = 0;
64 m_nBlock = 0;
65 m_aCustomPropertyName.clear();
66 m_nType = 0;
67 m_nInBlock = 0;
68 m_CustomStringPropertyState = NONE;
71 void OOXMLDocPropHandler::AddCustomProperty( const uno::Any& aAny )
73 if ( m_aCustomPropertyName.isEmpty() )
74 return;
76 const uno::Reference< beans::XPropertyContainer > xUserProps =
77 m_xDocProp->getUserDefinedProperties();
78 if ( !xUserProps.is() )
79 throw uno::RuntimeException();
81 try
83 xUserProps->addProperty( m_aCustomPropertyName,
84 beans::PropertyAttribute::REMOVABLE, aAny );
86 catch( beans::PropertyExistException& )
88 // conflicts with core and extended properties are possible
90 catch( uno::Exception& )
92 OSL_FAIL( "Can not add custom property!" );
96 util::DateTime OOXMLDocPropHandler::GetDateTimeFromW3CDTF( std::u16string_view aChars )
98 oslDateTime aOslDTime = { 0, 0, 0, 0, 0, 0, 0, 0 };
99 const size_t nLen = aChars.size();
100 if ( nLen >= 4 )
102 aOslDTime.Year = static_cast<sal_Int16>(o3tl::toInt32(aChars.substr( 0, 4 )));
104 if ( nLen >= 7 && aChars[4] == '-' )
106 aOslDTime.Month = static_cast<sal_uInt16>(o3tl::toInt32(aChars.substr( 5, 2 )));
108 if ( nLen >= 10 && aChars[7] == '-' )
110 aOslDTime.Day = static_cast<sal_uInt16>(o3tl::toInt32(aChars.substr( 8, 2 )));
112 if ( nLen >= 16 && aChars[10] == 'T' && aChars[13] == ':' )
114 aOslDTime.Hours = static_cast<sal_uInt16>(o3tl::toInt32(aChars.substr( 11, 2 )));
115 aOslDTime.Minutes = static_cast<sal_uInt16>(o3tl::toInt32(aChars.substr( 14, 2 )));
117 size_t nOptTime = 0;
118 if ( nLen >= 19 && aChars[16] == ':' )
120 aOslDTime.Seconds = static_cast<sal_uInt16>(o3tl::toInt32(aChars.substr( 17, 2 )));
121 nOptTime += 3;
122 if ( nLen >= 20 && aChars[19] == '.' )
124 nOptTime += 1;
125 size_t digitPos = 20;
126 while (nLen > digitPos && digitPos < 29)
128 sal_Unicode c = aChars[digitPos];
129 if ( c < '0' || c > '9')
130 break;
131 aOslDTime.NanoSeconds *= 10;
132 aOslDTime.NanoSeconds += c - '0';
133 ++digitPos;
135 if ( digitPos < 29 )
137 // read less digits than 9
138 // add correct exponent of 10
139 nOptTime += digitPos - 20;
140 for(; digitPos<29; ++digitPos)
142 aOslDTime.NanoSeconds *= 10;
145 else
147 //skip digits with more precision than we can handle
148 while(nLen > digitPos)
150 sal_Unicode c = aChars[digitPos];
151 if ( c < '0' || c > '9')
152 break;
153 ++digitPos;
155 nOptTime += digitPos - 20;
160 sal_Int32 nModif = 0;
161 if ( nLen >= 16 + nOptTime + 6 )
163 if ( ( aChars[16 + nOptTime] == '+' || aChars[16 + nOptTime] == '-' )
164 && aChars[16 + nOptTime + 3] == ':' )
166 nModif = o3tl::toInt32(aChars.substr( 16 + nOptTime + 1, 2 )) * 3600;
167 nModif += o3tl::toInt32(aChars.substr( 16 + nOptTime + 4, 2 )) * 60;
168 if ( aChars[16 + nOptTime] == '-' )
169 nModif *= -1;
173 if ( nModif )
175 // convert to UTC time
176 TimeValue aTmp;
177 if ( osl_getTimeValueFromDateTime( &aOslDTime, &aTmp ) )
179 aTmp.Seconds -= nModif;
180 osl_getDateTimeFromTimeValue( &aTmp, &aOslDTime );
188 return util::DateTime( aOslDTime.NanoSeconds, aOslDTime.Seconds,
189 aOslDTime.Minutes, aOslDTime.Hours,
190 aOslDTime.Day, aOslDTime.Month, aOslDTime.Year, false);
193 uno::Sequence< OUString > OOXMLDocPropHandler::GetKeywordsSet( std::u16string_view aChars )
195 if ( !aChars.empty() )
197 std::string aUtf8Chars( OUStringToOString( aChars, RTL_TEXTENCODING_UTF8 ) );
198 std::vector<std::string> aUtf8Result;
199 boost::split( aUtf8Result, aUtf8Chars, boost::is_any_of(" ,;:\t"), boost::token_compress_on );
201 if (!aUtf8Result.empty())
203 uno::Sequence< OUString > aResult( aUtf8Result.size() );
204 OUString* pResultValues = aResult.getArray();
205 for (auto const& elem : aUtf8Result)
207 *pResultValues = OUString( elem.c_str(), static_cast< sal_Int32 >( elem.size() ),RTL_TEXTENCODING_UTF8 );
208 ++pResultValues;
211 return aResult;
214 return uno::Sequence< OUString >();
217 void OOXMLDocPropHandler::UpdateDocStatistic( std::u16string_view aChars )
219 uno::Sequence< beans::NamedValue > aSet = m_xDocProp->getDocumentStatistics();
220 OUString aName;
222 switch( m_nBlock )
224 case EXTPR_TOKEN( Characters ):
225 aName = "NonWhitespaceCharacterCount";
226 break;
228 case EXTPR_TOKEN( CharactersWithSpaces ):
229 aName = "CharacterCount";
230 break;
232 case EXTPR_TOKEN( Pages ):
233 aName = "PageCount";
234 break;
236 case EXTPR_TOKEN( Words ):
237 aName = "WordCount";
238 break;
240 case EXTPR_TOKEN( Paragraphs ):
241 aName = "ParagraphCount";
242 break;
244 default:
245 OSL_FAIL( "Unexpected statistic!" );
246 break;
249 if ( aName.isEmpty() )
250 return;
252 sal_Int32 nInd = 0;
253 for ( auto pProp = aSet.getConstArray(); nInd < aSet.getLength(); ++nInd )
254 if ( pProp[nInd].Name == aName )
255 break;
257 if (nInd == aSet.getLength())
258 aSet.realloc( nInd + 1 );
260 aSet.getArray()[nInd] = { aName, uno::Any(o3tl::toInt32(aChars)) };
262 m_xDocProp->setDocumentStatistics( aSet );
265 // com.sun.star.xml.sax.XFastDocumentHandler
267 void SAL_CALL OOXMLDocPropHandler::startDocument()
271 void SAL_CALL OOXMLDocPropHandler::endDocument()
273 InitNew();
276 void OOXMLDocPropHandler::processingInstruction( const OUString& /*rTarget*/, const OUString& /*rData*/ )
280 void SAL_CALL OOXMLDocPropHandler::setDocumentLocator( const uno::Reference< xml::sax::XLocator >& )
284 // com.sun.star.xml.sax.XFastContextHandler
286 void SAL_CALL OOXMLDocPropHandler::startFastElement( ::sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttribs )
288 if ( !m_nInBlock && !m_nState )
290 if ( nElement == COREPR_TOKEN( coreProperties )
291 || nElement == EXTPR_TOKEN( Properties )
292 || nElement == CUSTPR_TOKEN( Properties ) )
294 m_nState = nElement;
296 else
298 OSL_FAIL( "Unexpected file format!" );
301 else if ( m_nState && m_nInBlock == 1 ) // that tag should contain the property name
303 // Currently the attributes are ignored for the core properties since the only
304 // known attribute is xsi:type that can only be used with dcterms:created and
305 // dcterms:modified, and this element is allowed currently to have only one value dcterms:W3CDTF
306 m_nBlock = nElement;
308 if ( xAttribs.is() && xAttribs->hasAttribute( XML_name ) )
309 m_aCustomPropertyName = xAttribs->getValue( XML_name );
311 else if ( m_nState && m_nInBlock == 2 && getNamespace( nElement ) == NMSP_officeDocPropsVT )
313 m_nType = nElement;
315 // variant tags in vector
316 else if ( m_nState && m_nInBlock == 3 && getNamespace( nElement ) == NMSP_officeDocPropsVT )
318 m_nType = nElement;
320 // lpstr or i4 tags in vector
321 else if ( m_nState && m_nInBlock == 4 && getNamespace( nElement ) == NMSP_officeDocPropsVT )
323 m_nType = nElement;
325 else
327 SAL_WARN("oox", "OOXMLDocPropHandler::startFastElement: unknown element " << getBaseToken(nElement) << " m_nState=" << m_nState << " m_nInBlock=" << m_nInBlock);
330 if ( m_nInBlock == SAL_MAX_INT32 )
331 throw uno::RuntimeException();
333 m_nInBlock++;
336 void SAL_CALL OOXMLDocPropHandler::startUnknownElement( const OUString& aNamespace, const OUString& aName, const uno::Reference< xml::sax::XFastAttributeList >& )
338 SAL_WARN("oox", "Unknown element " << aNamespace << ":" << aName);
340 if ( m_nInBlock == SAL_MAX_INT32 )
341 throw uno::RuntimeException();
343 m_nInBlock++;
346 void SAL_CALL OOXMLDocPropHandler::endFastElement( ::sal_Int32 )
348 if ( !m_nInBlock )
349 return;
351 m_nInBlock--;
353 if ( !m_nInBlock )
354 m_nState = 0;
355 else if ( m_nInBlock == 1 )
357 m_nBlock = 0;
358 m_aCustomPropertyName.clear();
360 else if ( m_nInBlock == 2 )
362 if ( m_nState == CUSTPR_TOKEN(Properties)
363 && m_nBlock == CUSTPR_TOKEN(property))
365 switch (m_nType)
367 case VT_TOKEN(bstr):
368 case VT_TOKEN(lpstr):
369 case VT_TOKEN(lpwstr):
370 if (!m_aCustomPropertyName.isEmpty() &&
371 INSERTED != m_CustomStringPropertyState)
373 // the property has string type, so it is valid
374 // even with an empty value - characters() has
375 // not been called in that case
376 AddCustomProperty(uno::Any(OUString()));
378 break;
381 m_CustomStringPropertyState = NONE;
382 m_nType = 0;
386 void SAL_CALL OOXMLDocPropHandler::endUnknownElement( const OUString&, const OUString& )
388 if ( m_nInBlock )
389 m_nInBlock--;
392 uno::Reference< xml::sax::XFastContextHandler > SAL_CALL OOXMLDocPropHandler::createFastChildContext( ::sal_Int32, const uno::Reference< xml::sax::XFastAttributeList >& )
394 // Should the arguments be parsed?
395 return uno::Reference< xml::sax::XFastContextHandler >( static_cast< xml::sax::XFastContextHandler* >( this ) );
398 uno::Reference< xml::sax::XFastContextHandler > SAL_CALL OOXMLDocPropHandler::createUnknownChildContext( const OUString&, const OUString&, const uno::Reference< xml::sax::XFastAttributeList >& )
400 return uno::Reference< xml::sax::XFastContextHandler >( static_cast< xml::sax::XFastContextHandler* >( this ) );
403 void SAL_CALL OOXMLDocPropHandler::characters( const OUString& aChars )
407 if ( (m_nInBlock == 2) || ((m_nInBlock == 3) && m_nType) )
409 if ( m_nState == COREPR_TOKEN( coreProperties ) )
411 switch( m_nBlock )
413 case COREPR_TOKEN( category ):
414 m_aCustomPropertyName = "OOXMLCorePropertyCategory";
415 AddCustomProperty( uno::Any( aChars ) ); // the property has string type
416 break;
418 case COREPR_TOKEN( contentStatus ):
419 m_aCustomPropertyName = "OOXMLCorePropertyContentStatus";
420 AddCustomProperty( uno::Any( aChars ) ); // the property has string type
421 break;
423 case COREPR_TOKEN( contentType ):
424 m_aCustomPropertyName = "OOXMLCorePropertyContentType";
425 AddCustomProperty( uno::Any( aChars ) ); // the property has string type
426 break;
428 case DC_TOKEN( identifier ):
429 m_aCustomPropertyName = "OOXMLCorePropertyIdentifier";
430 AddCustomProperty( uno::Any( aChars ) ); // the property has string type
431 break;
433 case COREPR_TOKEN( version ):
434 m_aCustomPropertyName = "OOXMLCorePropertyVersion";
435 AddCustomProperty( uno::Any( aChars ) ); // the property has string type
436 break;
438 case DCT_TOKEN( created ):
439 if ( aChars.getLength() >= 4 )
440 m_xDocProp->setCreationDate( GetDateTimeFromW3CDTF( aChars ) );
441 break;
443 case DC_TOKEN( creator ):
444 m_xDocProp->setAuthor( aChars );
445 break;
447 case DC_TOKEN( description ):
448 m_xDocProp->setDescription( aChars );
449 break;
451 case COREPR_TOKEN( keywords ):
452 m_xDocProp->setKeywords( GetKeywordsSet( aChars ) );
453 break;
455 case DC_TOKEN( language ):
456 if ( aChars.getLength() >= 2 )
457 m_xDocProp->setLanguage( LanguageTag::convertToLocale( aChars) );
458 break;
460 case COREPR_TOKEN( lastModifiedBy ):
461 m_xDocProp->setModifiedBy( aChars );
462 break;
464 case COREPR_TOKEN( lastPrinted ):
465 if ( aChars.getLength() >= 4 )
466 m_xDocProp->setPrintDate( GetDateTimeFromW3CDTF( aChars ) );
467 break;
469 case DCT_TOKEN( modified ):
470 if ( aChars.getLength() >= 4 )
471 m_xDocProp->setModificationDate( GetDateTimeFromW3CDTF( aChars ) );
472 break;
474 case COREPR_TOKEN( revision ):
477 m_xDocProp->setEditingCycles(
478 static_cast<sal_Int16>(aChars.toInt32()) );
480 catch (lang::IllegalArgumentException &)
482 // ignore
484 break;
486 case DC_TOKEN( subject ):
487 m_xDocProp->setSubject( m_xDocProp->getSubject() + aChars );
488 break;
490 case DC_TOKEN( title ):
491 m_xDocProp->setTitle( m_xDocProp->getTitle() + aChars );
492 break;
494 default:
495 OSL_FAIL( "Unexpected core property!" );
498 else if ( m_nState == EXTPR_TOKEN( Properties ) )
500 switch( m_nBlock )
502 case EXTPR_TOKEN( Application ):
503 m_xDocProp->setGenerator( aChars );
504 break;
506 case EXTPR_TOKEN( Template ):
507 m_xDocProp->setTemplateName( aChars );
508 break;
510 case EXTPR_TOKEN( TotalTime ):
512 sal_Int32 nDuration;
513 if (!o3tl::checked_multiply<sal_Int32>(aChars.toInt32(), 60, nDuration))
517 // The TotalTime is in mins as per ECMA specification.
518 m_xDocProp->setEditingDuration(nDuration);
520 catch (const lang::IllegalArgumentException&)
522 // ignore
525 break;
527 case EXTPR_TOKEN( Characters ):
528 case EXTPR_TOKEN( CharactersWithSpaces ):
529 case EXTPR_TOKEN( Pages ):
530 case EXTPR_TOKEN( Words ):
531 case EXTPR_TOKEN( Paragraphs ):
532 UpdateDocStatistic( aChars );
533 break;
535 case EXTPR_TOKEN( HyperlinksChanged ):
536 m_aCustomPropertyName = "HyperlinksChanged";
537 // tdf#103987 Don't create custom property if the value is default
538 if ( aChars.toBoolean() )
539 AddCustomProperty( uno::Any( aChars.toBoolean() ) ); // the property has boolean type
540 break;
542 case EXTPR_TOKEN( LinksUpToDate ):
543 m_aCustomPropertyName = "LinksUpToDate";
544 // tdf#103987 Don't create custom property if the value is default
545 if ( aChars.toBoolean() )
546 AddCustomProperty( uno::Any( aChars.toBoolean() ) ); // the property has boolean type
547 break;
549 case EXTPR_TOKEN( ScaleCrop ):
550 m_aCustomPropertyName = "ScaleCrop";
551 // tdf#103987 Don't create custom property if the value is default
552 if ( aChars.toBoolean() )
553 AddCustomProperty( uno::Any( aChars.toBoolean() ) ); // the property has boolean type
554 break;
556 case EXTPR_TOKEN( SharedDoc ):
557 m_aCustomPropertyName = "ShareDoc";
558 // tdf#103987 Don't create custom property if the value is default
559 if ( aChars.toBoolean() )
560 AddCustomProperty( uno::Any( aChars.toBoolean() ) ); // the property has boolean type
561 break;
563 case EXTPR_TOKEN( DocSecurity ):
564 m_aCustomPropertyName = "DocSecurity";
565 // tdf#103987 Don't create custom property if the value is default
566 // OOXTODO Instead of storing value, enable security
567 // 1 - password protected, 2 - recommended read-only
568 // 4 - enforced read-only, 8 - locked for annotation
569 if ( aChars.toInt32() != 0 )
570 AddCustomProperty( uno::Any( aChars.toInt32() ) ); // the property has sal_Int32 type
571 break;
573 case EXTPR_TOKEN( HiddenSlides ):
574 m_aCustomPropertyName = "HiddenSlides";
575 // tdf#103987 Don't create custom property if the value is default
576 if ( aChars.toInt32() != 0 )
577 AddCustomProperty( uno::Any( aChars.toInt32() ) ); // the property has sal_Int32 type
578 break;
580 case EXTPR_TOKEN( MMClips ):
581 m_aCustomPropertyName = "MMClips";
582 // tdf#103987 Don't create custom property if the value is default
583 if ( aChars.toInt32() != 0 )
584 AddCustomProperty( uno::Any( aChars.toInt32() ) ); // the property has sal_Int32 type
585 break;
587 case EXTPR_TOKEN( Notes ):
588 m_aCustomPropertyName = "Notes";
589 // tdf#103987 Don't create custom property if the value is default
590 if ( aChars.toInt32() != 0 )
591 AddCustomProperty( uno::Any( aChars.toInt32() ) ); // the property has sal_Int32 type
592 break;
594 case EXTPR_TOKEN( Slides ):
595 m_aCustomPropertyName = "Slides";
596 // tdf#103987 Don't create custom property if the value is default
597 if ( aChars.toInt32() != 0 )
598 AddCustomProperty( uno::Any( aChars.toInt32() ) ); // the property has sal_Int32 type
599 break;
601 case EXTPR_TOKEN( AppVersion ):
602 m_aCustomPropertyName = "AppVersion";
603 AddCustomProperty( uno::Any( aChars ) ); // the property has string type
604 break;
606 case EXTPR_TOKEN( Company ):
607 m_aCustomPropertyName = "Company";
608 AddCustomProperty( uno::Any( aChars ) ); // the property has string type
609 break;
611 case EXTPR_TOKEN( HyperlinkBase ):
612 m_aCustomPropertyName = "HyperlinkBase";
613 AddCustomProperty( uno::Any( aChars ) ); // the property has string type
614 break;
616 case EXTPR_TOKEN( Manager ):
617 m_aCustomPropertyName = "Manager";
618 AddCustomProperty( uno::Any( aChars ) ); // the property has string type
619 break;
621 case EXTPR_TOKEN( PresentationFormat ):
622 m_aCustomPropertyName = "PresentationFormat";
623 AddCustomProperty( uno::Any( aChars ) ); // the property has string type
624 break;
626 case EXTPR_TOKEN( Lines ):
627 case EXTPR_TOKEN( DigSig ):
628 case EXTPR_TOKEN( HeadingPairs ):
629 case EXTPR_TOKEN( HLinks ):
630 case EXTPR_TOKEN( TitlesOfParts ):
631 // ignored during the import currently
632 break;
634 default:
635 OSL_FAIL( "Unexpected extended property!" );
638 else if ( m_nState == CUSTPR_TOKEN( Properties ) )
640 if ( m_nBlock == CUSTPR_TOKEN( property ) )
642 // this is a custom property
643 switch( m_nType )
645 case VT_TOKEN( bool ):
646 AddCustomProperty( uno::Any( aChars.toBoolean() ) );
647 break;
649 case VT_TOKEN( bstr ):
650 case VT_TOKEN( lpstr ):
651 case VT_TOKEN( lpwstr ):
652 // the property has string type
653 AddCustomProperty( uno::Any( AttributeConversion::decodeXString( aChars ) ) );
654 m_CustomStringPropertyState = INSERTED;
655 break;
657 case VT_TOKEN( date ):
658 case VT_TOKEN( filetime ):
659 AddCustomProperty( uno::Any( GetDateTimeFromW3CDTF( aChars ) ) );
660 break;
662 case VT_TOKEN( i1 ):
663 case VT_TOKEN( i2 ):
664 AddCustomProperty( uno::Any( static_cast<sal_Int16>(aChars.toInt32()) ) );
665 break;
667 case VT_TOKEN( i4 ):
668 case VT_TOKEN( int ):
669 AddCustomProperty( uno::Any( aChars.toInt32() ) );
670 break;
672 case VT_TOKEN( i8 ):
673 AddCustomProperty( uno::Any( aChars.toInt64() ) );
674 break;
676 case VT_TOKEN( r4 ):
677 AddCustomProperty( uno::Any( aChars.toFloat() ) );
678 break;
680 case VT_TOKEN( r8 ):
681 AddCustomProperty( uno::Any( aChars.toDouble() ) );
682 break;
684 default:
685 // all the other types are ignored;
686 break;
689 else
691 OSL_FAIL( "Unexpected tag in custom property!" );
696 catch( uno::RuntimeException& )
698 throw;
700 catch( xml::sax::SAXException& )
702 throw;
704 catch( uno::Exception& )
706 css::uno::Any anyEx = cppu::getCaughtException();
707 throw xml::sax::SAXException(
708 "Error while setting document property!",
709 uno::Reference< uno::XInterface >(),
710 anyEx );
714 } // namespace oox::docprop
716 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */