1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 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/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 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 . 18 */ 19 20 #include <oox/core/xmlfilterbase.hxx> 21 22 #include <cstdio> 23 #include <string_view> 24 25 #include <com/sun/star/beans/XPropertyAccess.hpp> 26 #include <com/sun/star/beans/XPropertySet.hpp> 27 #include <com/sun/star/beans/Pair.hpp> 28 #include <com/sun/star/embed/XRelationshipAccess.hpp> 29 #include <com/sun/star/frame/XModel.hpp> 30 #include <com/sun/star/xml/sax/XFastSAXSerializable.hpp> 31 #include <com/sun/star/xml/sax/XSAXSerializable.hpp> 32 #include <com/sun/star/xml/sax/Writer.hpp> 33 #include <o3tl/any.hxx> 34 #include <unotools/mediadescriptor.hxx> 35 #include <unotools/docinfohelper.hxx> 36 #include <sax/fshelper.hxx> 37 #include <rtl/strbuf.hxx> 38 #include <rtl/ustrbuf.hxx> 39 #include <rtl/instance.hxx> 40 #include <osl/diagnose.h> 41 #include <sal/log.hxx> 42 #include <i18nlangtag/languagetag.hxx> 43 #include <oox/core/fastparser.hxx> 44 #include <oox/core/fragmenthandler.hxx> 45 #include <oox/core/recordparser.hxx> 46 #include <oox/core/relationshandler.hxx> 47 #include <oox/helper/propertyset.hxx> 48 #include <oox/helper/zipstorage.hxx> 49 #include <oox/ole/olestorage.hxx> 50 #include <oox/token/namespaces.hxx> 51 #include <oox/token/relationship.hxx> 52 #include <oox/token/properties.hxx> 53 #include <oox/token/tokens.hxx> 54 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> 55 #include <com/sun/star/document/XOOXMLDocumentPropertiesImporter.hpp> 56 #include <com/sun/star/xml/dom/DocumentBuilder.hpp> 57 #include <comphelper/processfactory.hxx> 58 #include <oox/core/filterdetect.hxx> 59 #include <comphelper/storagehelper.hxx> 60 #include <comphelper/sequence.hxx> 61 #include <comphelper/ofopxmlhelper.hxx> 62 63 #include <oox/crypto/DocumentEncryption.hxx> 64 #include <tools/urlobj.hxx> 65 #include <com/sun/star/util/Date.hpp> 66 #include <com/sun/star/util/Duration.hpp> 67 #include <sax/tools/converter.hxx> 68 #include <oox/token/namespacemap.hxx> 69 #include <editeng/unoprnms.hxx> 70 #include <o3tl/sorted_vector.hxx> 71 72 using ::com::sun::star::xml::dom::DocumentBuilder; 73 using ::com::sun::star::xml::dom::XDocument; 74 using ::com::sun::star::xml::dom::XDocumentBuilder; 75 76 namespace oox::core { 77 78 using namespace ::com::sun::star; 79 using namespace ::com::sun::star::beans; 80 using namespace ::com::sun::star::container; 81 using namespace ::com::sun::star::document; 82 using namespace ::com::sun::star::embed; 83 using namespace ::com::sun::star::io; 84 using namespace ::com::sun::star::lang; 85 using namespace ::com::sun::star::uno; 86 using namespace ::com::sun::star::xml::sax; 87 88 using utl::MediaDescriptor; 89 using ::sax_fastparser::FSHelperPtr; 90 using ::sax_fastparser::FastSerializerHelper; 91 92 namespace { 93 94 struct NamespaceIds: public rtl::StaticWithInit< 95 Sequence< beans::Pair< OUString, sal_Int32 > >, 96 NamespaceIds> 97 { 98 Sequence< beans::Pair< OUString, sal_Int32 > > operator()() 99 { 100 return css::uno::Sequence<css::beans::Pair<OUString, sal_Int32>>{ 101 {"http://www.w3.org/XML/1998/namespace", NMSP_xml}, 102 {"http://schemas.openxmlformats.org/package/2006/relationships", 103 NMSP_packageRel}, 104 {"http://schemas.openxmlformats.org/officeDocument/2006/relationships", 105 NMSP_officeRel}, 106 {"http://purl.oclc.org/ooxml/officeDocument/relationships", 107 NMSP_officeRel}, 108 {"http://schemas.openxmlformats.org/drawingml/2006/main", NMSP_dml}, 109 {"http://purl.oclc.org/ooxml/drawingml/main", NMSP_dml}, 110 {"http://schemas.openxmlformats.org/drawingml/2006/diagram", 111 NMSP_dmlDiagram}, 112 {"http://purl.oclc.org/ooxml/drawingml/diagram", NMSP_dmlDiagram}, 113 {"http://schemas.openxmlformats.org/drawingml/2006/chart", 114 NMSP_dmlChart}, 115 {"http://schemas.openxmlformats.org/drawingml/2006/chartDrawing", 116 NMSP_dmlChartDr}, 117 {"urn:schemas-microsoft-com:vml", NMSP_vml}, 118 {"urn:schemas-microsoft-com:office:office", NMSP_vmlOffice}, 119 {"urn:schemas-microsoft-com:office:word", NMSP_vmlWord}, 120 {"urn:schemas-microsoft-com:office:excel", NMSP_vmlExcel}, 121 {"urn:schemas-microsoft-com:office:powerpoint", NMSP_vmlPowerpoint}, 122 {"http://schemas.microsoft.com/office/2006/activeX", NMSP_ax}, 123 {"http://schemas.openxmlformats.org/spreadsheetml/2006/main", 124 NMSP_xls}, 125 {"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing", 126 NMSP_xm}, 127 {"http://schemas.microsoft.com/office/excel/2006/main", 128 NMSP_dmlSpreadDr}, 129 {"http://schemas.openxmlformats.org/presentationml/2006/main", 130 NMSP_ppt}, 131 {"http://schemas.openxmlformats.org/markup-compatibility/2006", 132 NMSP_mce}, 133 {"http://schemas.openxmlformats.org/spreadsheetml/2006/main/v2", 134 NMSP_mceTest}, 135 {"http://schemas.openxmlformats.org/officeDocument/2006/math", 136 NMSP_officeMath}, 137 {"http://schemas.microsoft.com/office/drawing/2008/diagram", 138 NMSP_dsp}, 139 {"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main", 140 NMSP_xls14Lst}, 141 {"http://schemas.libreoffice.org/", NMSP_loext}, 142 {"http://schemas.microsoft.com/office/drawing/2010/main", 143 NMSP_a14}, 144 {"http://schemas.microsoft.com/office/powerpoint/2010/main", 145 NMSP_p14}, 146 {"http://schemas.microsoft.com/office/powerpoint/2012/main", 147 NMSP_p15}, 148 {"http://schemas.microsoft.com/office/spreadsheetml/2011/1/ac", 149 NMSP_x12ac}, 150 {"http://schemas.microsoft.com/office/drawing/2012/chart", 151 NMSP_c15}, 152 }; 153 } 154 }; 155 156 void registerNamespaces( FastParser& rParser ) 157 { 158 const Sequence< beans::Pair<OUString, sal_Int32> > ids = NamespaceIds::get(); 159 160 // Filter out duplicates: a namespace can have multiple URLs, think of 161 // strict vs transitional. 162 o3tl::sorted_vector<sal_Int32> aSet; 163 aSet.reserve(ids.getLength()); 164 for (const auto& rId : ids) 165 aSet.insert(rId.Second); 166 167 for (auto const& elem : aSet) 168 rParser.registerNamespace(elem); 169 } 170 171 } // namespace 172 173 struct XmlFilterBaseImpl 174 { 175 typedef RefMap< OUString, Relations > RelationsMap; 176 177 FastParser maFastParser; 178 RelationsMap maRelationsMap; 179 TextFieldStack maTextFieldStack; 180 const NamespaceMap& mrNamespaceMap; 181 NamedShapePairs* mpDiagramFontHeights = nullptr; 182 183 /// @throws RuntimeException 184 explicit XmlFilterBaseImpl(); 185 }; 186 187 constexpr OUStringLiteral gaBinSuffix( u".bin" ); 188 189 XmlFilterBaseImpl::XmlFilterBaseImpl() : 190 mrNamespaceMap(StaticNamespaceMap::get()) 191 { 192 // register XML namespaces 193 registerNamespaces(maFastParser); 194 } 195 196 XmlFilterBase::XmlFilterBase( const Reference< XComponentContext >& rxContext ) : 197 FilterBase( rxContext ), 198 mxImpl( new XmlFilterBaseImpl ), 199 mnRelId( 1 ), 200 mnMaxDocId( 0 ), 201 mbMSO2007(false), 202 mbMissingExtDrawing(false) 203 { 204 } 205 206 XmlFilterBase::~XmlFilterBase() 207 { 208 // #i118640# Reset the DocumentHandler at the FastSaxParser manually; this is 209 // needed since the mechanism is that instances of FragmentHandler execute 210 // their stuff (creating objects, setting attributes, ...) on being destroyed. 211 // They get destroyed by setting a new DocumentHandler. This also happens in 212 // the following implicit destruction chain of ~XmlFilterBaseImpl, but in that 213 // case it's member RelationsMap maRelationsMap will be destroyed, but maybe 214 // still be used by ~FragmentHandler -> crash. 215 mxImpl->maFastParser.clearDocumentHandler(); 216 } 217 218 void XmlFilterBase::checkDocumentProperties(const Reference<XDocumentProperties>& xDocProps) 219 { 220 mbMSO2007 = false; 221 if (!xDocProps->getGenerator().startsWithIgnoreAsciiCase("Microsoft")) 222 return; 223 224 uno::Reference<beans::XPropertyAccess> xUserDefProps(xDocProps->getUserDefinedProperties(), uno::UNO_QUERY); 225 if (!xUserDefProps.is()) 226 return; 227 228 comphelper::SequenceAsHashMap aUserDefinedProperties(xUserDefProps->getPropertyValues()); 229 comphelper::SequenceAsHashMap::iterator it = aUserDefinedProperties.find("AppVersion"); 230 if (it == aUserDefinedProperties.end()) 231 return; 232 233 OUString aValue; 234 if (!(it->second >>= aValue)) 235 return; 236 237 if (!aValue.startsWithIgnoreAsciiCase("12.")) 238 return; 239 240 SAL_INFO("oox", "a MSO 2007 document"); 241 mbMSO2007 = true; 242 } 243 244 void XmlFilterBase::putPropertiesToDocumentGrabBag(const css::uno::Reference<css::lang::XComponent>& xDstDoc, 245 const comphelper::SequenceAsHashMap& rProperties) 246 { 247 try 248 { 249 uno::Reference<beans::XPropertySet> xDocProps(xDstDoc, uno::UNO_QUERY); 250 if (xDocProps.is()) 251 { 252 uno::Reference<beans::XPropertySetInfo> xPropsInfo = xDocProps->getPropertySetInfo(); 253 254 static constexpr OUStringLiteral aGrabBagPropName = u"InteropGrabBag"; 255 if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(aGrabBagPropName)) 256 { 257 // get existing grab bag 258 comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(aGrabBagPropName)); 259 260 // put the new items 261 aGrabBag.update(rProperties); 262 263 // put it back to the document 264 xDocProps->setPropertyValue(aGrabBagPropName, uno::Any(aGrabBag.getAsConstPropertyValueList())); 265 } 266 } 267 } 268 catch (const uno::Exception&) 269 { 270 SAL_WARN("oox","Failed to save documents grab bag"); 271 } 272 } 273 274 void XmlFilterBase::importDocumentProperties() 275 { 276 MediaDescriptor aMediaDesc( getMediaDescriptor() ); 277 Reference< XInputStream > xInputStream; 278 Reference< XComponentContext > xContext = getComponentContext(); 279 rtl::Reference< ::oox::core::FilterDetect > xDetector( new ::oox::core::FilterDetect( xContext ) ); 280 xInputStream = xDetector->extractUnencryptedPackage( aMediaDesc ); 281 Reference< XComponent > xModel = getModel(); 282 Reference< XStorage > xDocumentStorage ( 283 ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( OFOPXML_STORAGE_FORMAT_STRING, xInputStream ) ); 284 Reference< XInterface > xTemp = xContext->getServiceManager()->createInstanceWithContext( 285 "com.sun.star.document.OOXMLDocumentPropertiesImporter", 286 xContext); 287 Reference< XOOXMLDocumentPropertiesImporter > xImporter( xTemp, UNO_QUERY ); 288 Reference< XDocumentPropertiesSupplier > xPropSupplier( xModel, UNO_QUERY); 289 Reference< XDocumentProperties > xDocProps = xPropSupplier->getDocumentProperties(); 290 xImporter->importProperties( xDocumentStorage, xDocProps ); 291 checkDocumentProperties(xDocProps); 292 293 importCustomFragments(xDocumentStorage); 294 } 295 296 FastParser* XmlFilterBase::createParser() 297 { 298 FastParser* pParser = new FastParser; 299 registerNamespaces(*pParser); 300 return pParser; 301 } 302 303 namespace { 304 305 OUString getTransitionalRelationshipOfficeDocType(std::u16string_view rPart) 306 { 307 return OUString::Concat("http://schemas.openxmlformats.org/officeDocument/2006/relationships/") 308 + rPart; 309 } 310 311 OUString getStrictRelationshipOfficeDocType(std::u16string_view rPart) 312 { 313 return OUString::Concat("http://purl.oclc.org/ooxml/officeDocument/relationships/") + rPart; 314 } 315 316 } 317 318 OUString XmlFilterBase::getFragmentPathFromFirstTypeFromOfficeDoc( std::u16string_view rPart ) 319 { 320 // importRelations() caches the relations map for subsequence calls 321 const OUString aTransitionalRelationshipType = getTransitionalRelationshipOfficeDocType(rPart); 322 OUString aFragment = importRelations( OUString() )->getFragmentPathFromFirstType( aTransitionalRelationshipType ); 323 if(aFragment.isEmpty()) 324 { 325 const OUString aStrictRelationshipType = getStrictRelationshipOfficeDocType(rPart); 326 aFragment = importRelations( OUString() )->getFragmentPathFromFirstType( aStrictRelationshipType ); 327 } 328 329 return aFragment; 330 } 331 332 bool XmlFilterBase::importFragment( const rtl::Reference<FragmentHandler>& rxHandler ) 333 { 334 FastParser aParser; 335 registerNamespaces(aParser); 336 return importFragment(rxHandler, aParser); 337 } 338 339 bool XmlFilterBase::importFragment( const rtl::Reference<FragmentHandler>& rxHandler, FastParser& rParser ) 340 { 341 OSL_ENSURE( rxHandler.is(), "XmlFilterBase::importFragment - missing fragment handler" ); 342 if( !rxHandler.is() ) 343 return false; 344 345 // fragment handler must contain path to fragment stream 346 OUString aFragmentPath = rxHandler->getFragmentPath(); 347 OSL_ENSURE( !aFragmentPath.isEmpty(), "XmlFilterBase::importFragment - missing fragment path" ); 348 if( aFragmentPath.isEmpty() ) 349 return false; 350 351 // try to import binary streams (fragment extension must be '.bin') 352 if (aFragmentPath.endsWith(gaBinSuffix)) 353 { 354 try 355 { 356 // try to open the fragment stream (this may fail - do not assert) 357 Reference< XInputStream > xInStrm( openInputStream( aFragmentPath ), UNO_SET_THROW ); 358 359 // create the record parser 360 RecordParser aParser; 361 aParser.setFragmentHandler( rxHandler ); 362 363 // create the input source and parse the stream 364 RecordInputSource aSource; 365 aSource.mxInStream = std::make_shared<BinaryXInputStream>( xInStrm, true ); 366 aSource.maSystemId = aFragmentPath; 367 aParser.parseStream( aSource ); 368 return true; 369 } 370 catch( Exception& ) 371 { 372 } 373 return false; 374 } 375 376 // get the XFastDocumentHandler interface from the fragment handler 377 if( !rxHandler.is() ) 378 return false; 379 380 // try to import XML stream 381 try 382 { 383 /* Try to open the fragment stream (may fail, do not throw/assert). 384 Using the virtual function openFragmentStream() allows a document 385 handler to create specialized input streams, e.g. VML streams that 386 have to preprocess the raw input data. */ 387 Reference< XInputStream > xInStrm = rxHandler->openFragmentStream(); 388 /* tdf#100084 Check again the aFragmentPath route with lowercase file name 389 TODO: complete handling of case-insensitive file paths */ 390 if ( !xInStrm.is() ) 391 { 392 sal_Int32 nPathLen = aFragmentPath.lastIndexOf('/') + 1; 393 OUString fileName = aFragmentPath.copy(nPathLen); 394 OUString sLowerCaseFileName = fileName.toAsciiLowerCase(); 395 if ( fileName != sLowerCaseFileName ) 396 { 397 aFragmentPath = aFragmentPath.subView(0, nPathLen) + sLowerCaseFileName; 398 xInStrm = openInputStream(aFragmentPath); 399 } 400 } 401 402 // own try/catch block for showing parser failure assertion with fragment path 403 if( xInStrm.is() ) try 404 { 405 rParser.setDocumentHandler(rxHandler); 406 rParser.parseStream(xInStrm, aFragmentPath); 407 return true; 408 } 409 catch( Exception& ) 410 { 411 OSL_FAIL( OStringBuffer( "XmlFilterBase::importFragment - XML parser failed in fragment '" ). 412 append( OUStringToOString( aFragmentPath, RTL_TEXTENCODING_ASCII_US ) ).append( '\'' ).getStr() ); 413 } 414 } 415 catch( Exception& ) 416 { 417 } 418 return false; 419 } 420 421 Reference<XDocument> XmlFilterBase::importFragment( const OUString& aFragmentPath ) 422 { 423 Reference<XDocument> xRet; 424 425 // path to fragment stream valid? 426 OSL_ENSURE( !aFragmentPath.isEmpty(), "XmlFilterBase::importFragment - empty fragment path" ); 427 if( aFragmentPath.isEmpty() ) 428 return xRet; 429 430 // try to open the fragment stream (this may fail - do not assert) 431 Reference< XInputStream > xInStrm = openInputStream( aFragmentPath ); 432 if( !xInStrm.is() ) 433 return xRet; 434 435 // binary streams (fragment extension is '.bin') currently not supported 436 if (aFragmentPath.endsWith(gaBinSuffix)) 437 return xRet; 438 439 // try to import XML stream 440 try 441 { 442 // create the dom parser 443 Reference<XDocumentBuilder> xDomBuilder( DocumentBuilder::create( getComponentContext() ) ); 444 445 // create DOM from fragment 446 xRet = xDomBuilder->parse(xInStrm); 447 } 448 catch( Exception& ) 449 { 450 } 451 452 return xRet; 453 } 454 455 bool XmlFilterBase::importFragment( const ::rtl::Reference< FragmentHandler >& rxHandler, 456 const Reference< XFastSAXSerializable >& rxSerializer ) 457 { 458 if( !rxHandler.is() ) 459 return false; 460 461 // try to import XML stream 462 try 463 { 464 rxSerializer->fastSerialize( rxHandler, 465 mxImpl->maFastParser.getTokenHandler(), 466 Sequence< StringPair >(), 467 NamespaceIds::get() ); 468 return true; 469 } 470 catch( Exception& ) 471 {} 472 473 return false; 474 } 475 476 RelationsRef XmlFilterBase::importRelations( const OUString& rFragmentPath ) 477 { 478 // try to find cached relations 479 RelationsRef& rxRelations = mxImpl->maRelationsMap[ rFragmentPath ]; 480 if( !rxRelations ) 481 { 482 // import and cache relations 483 rxRelations = std::make_shared<Relations>( rFragmentPath ); 484 importFragment( new RelationsFragment( *this, rxRelations ) ); 485 } 486 return rxRelations; 487 } 488 489 Reference< XOutputStream > XmlFilterBase::openFragmentStream( const OUString& rStreamName, const OUString& rMediaType ) 490 { 491 Reference< XOutputStream > xOutputStream = openOutputStream( rStreamName ); 492 PropertySet aPropSet( xOutputStream ); 493 aPropSet.setProperty( PROP_MediaType, rMediaType ); 494 return xOutputStream; 495 } 496 497 FSHelperPtr XmlFilterBase::openFragmentStreamWithSerializer( const OUString& rStreamName, const OUString& rMediaType ) 498 { 499 const bool bWriteHeader = rMediaType.indexOf( "vml" ) < 0 || rMediaType.indexOf( "+xml" ) >= 0; 500 return std::make_shared<FastSerializerHelper>( openFragmentStream( rStreamName, rMediaType ), bWriteHeader ); 501 } 502 503 TextFieldStack& XmlFilterBase::getTextFieldStack() const 504 { 505 return mxImpl->maTextFieldStack; 506 } 507 508 namespace { 509 510 OUString lclAddRelation( const Reference< XRelationshipAccess >& rRelations, sal_Int32 nId, const OUString& rType, const OUString& rTarget, bool bExternal ) 511 { 512 OUString sId = "rId" + OUString::number( nId ); 513 514 Sequence< StringPair > aEntry( bExternal ? 3 : 2 ); 515 aEntry[0].First = "Type"; 516 aEntry[0].Second = rType; 517 aEntry[1].First = "Target"; 518 aEntry[1].Second = INetURLObject::decode(rTarget, INetURLObject::DecodeMechanism::ToIUri, RTL_TEXTENCODING_UTF8); 519 if( bExternal ) 520 { 521 aEntry[2].First = "TargetMode"; 522 aEntry[2].Second = "External"; 523 } 524 rRelations->insertRelationshipByID( sId, aEntry, true ); 525 526 return sId; 527 } 528 529 } // namespace 530 531 OUString XmlFilterBase::addRelation( const OUString& rType, const OUString& rTarget ) 532 { 533 Reference< XRelationshipAccess > xRelations( getStorage()->getXStorage(), UNO_QUERY ); 534 if( xRelations.is() ) 535 return lclAddRelation( xRelations, mnRelId ++, rType, rTarget, false/*bExternal*/ ); 536 537 return OUString(); 538 } 539 540 OUString XmlFilterBase::addRelation( const Reference< XOutputStream >& rOutputStream, const OUString& rType, const OUString& rTarget, bool bExternal ) 541 { 542 sal_Int32 nId = 0; 543 544 PropertySet aPropSet( rOutputStream ); 545 if( aPropSet.is() ) 546 aPropSet.getProperty( nId, PROP_RelId ); 547 else 548 nId = mnRelId++; 549 550 Reference< XRelationshipAccess > xRelations( rOutputStream, UNO_QUERY ); 551 if( xRelations.is() ) 552 return lclAddRelation( xRelations, nId, rType, rTarget, bExternal ); 553 554 return OUString(); 555 } 556 557 static void 558 writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, std::u16string_view sValue ) 559 { 560 pDoc->startElement(nXmlElement); 561 pDoc->writeEscaped( sValue ); 562 pDoc->endElement( nXmlElement ); 563 } 564 565 static void 566 writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const sal_Int32 nValue ) 567 { 568 pDoc->startElement(nXmlElement); 569 pDoc->write( nValue ); 570 pDoc->endElement( nXmlElement ); 571 } 572 573 static void 574 writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const util::DateTime& rTime ) 575 { 576 if( rTime.Year == 0 ) 577 return; 578 579 if ( ( nXmlElement >> 16 ) != XML_dcterms ) 580 pDoc->startElement(nXmlElement); 581 else 582 pDoc->startElement(nXmlElement, FSNS(XML_xsi, XML_type), "dcterms:W3CDTF"); 583 584 char pStr[200]; 585 snprintf( pStr, sizeof( pStr ), "%d-%02d-%02dT%02d:%02d:%02dZ", 586 rTime.Year, rTime.Month, rTime.Day, 587 rTime.Hours, rTime.Minutes, rTime.Seconds ); 588 589 pDoc->write( pStr ); 590 591 pDoc->endElement( nXmlElement ); 592 } 593 594 static void 595 writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const Sequence< OUString >& aItems ) 596 { 597 if( !aItems.hasElements() ) 598 return; 599 600 OUStringBuffer sRep; 601 sRep.append( aItems[ 0 ] ); 602 603 for( const OUString& rItem : aItems ) 604 { 605 sRep.append( " " ).append( rItem ); 606 } 607 608 writeElement( pDoc, nXmlElement, sRep.makeStringAndClear() ); 609 } 610 611 static void 612 writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const LanguageTag& rLanguageTag ) 613 { 614 // dc:language, Dublin Core recommends "such as RFC 4646", which is BCP 47 615 // and obsoleted by RFC 5646, see 616 // http://dublincore.org/documents/dcmi-terms/#terms-language 617 // http://dublincore.org/documents/dcmi-terms/#elements-language 618 writeElement( pDoc, nXmlElement, rLanguageTag.getBcp47MS() ); 619 } 620 621 static void 622 writeCoreProperties( XmlFilterBase& rSelf, const Reference< XDocumentProperties >& xProperties ) 623 { 624 OUString sValue; 625 if( rSelf.getVersion() == oox::core::ISOIEC_29500_2008 ) 626 { 627 // The lowercase "officedocument" is intentional and according to the spec 628 // (although most other places are written "officeDocument") 629 sValue = "http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties"; 630 } 631 else 632 sValue = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; 633 634 rSelf.addRelation( sValue, "docProps/core.xml" ); 635 FSHelperPtr pCoreProps = rSelf.openFragmentStreamWithSerializer( 636 "docProps/core.xml", 637 "application/vnd.openxmlformats-package.core-properties+xml" ); 638 pCoreProps->startElementNS( XML_cp, XML_coreProperties, 639 FSNS(XML_xmlns, XML_cp), rSelf.getNamespaceURL(OOX_NS(packageMetaCorePr)), 640 FSNS(XML_xmlns, XML_dc), rSelf.getNamespaceURL(OOX_NS(dc)), 641 FSNS(XML_xmlns, XML_dcterms), rSelf.getNamespaceURL(OOX_NS(dcTerms)), 642 FSNS(XML_xmlns, XML_dcmitype), rSelf.getNamespaceURL(OOX_NS(dcmiType)), 643 FSNS(XML_xmlns, XML_xsi), rSelf.getNamespaceURL(OOX_NS(xsi))); 644 645 uno::Reference<beans::XPropertyAccess> xUserDefinedProperties(xProperties->getUserDefinedProperties(), uno::UNO_QUERY); 646 comphelper::SequenceAsHashMap aUserDefinedProperties(xUserDefinedProperties->getPropertyValues()); 647 comphelper::SequenceAsHashMap::iterator it; 648 649 it = aUserDefinedProperties.find("OOXMLCorePropertyCategory"); 650 if (it != aUserDefinedProperties.end()) 651 { 652 OUString aValue; 653 if (it->second >>= aValue) 654 writeElement( pCoreProps, FSNS( XML_cp, XML_category ), aValue ); 655 } 656 657 it = aUserDefinedProperties.find("OOXMLCorePropertyContentStatus"); 658 if (it != aUserDefinedProperties.end()) 659 { 660 OUString aValue; 661 if (it->second >>= aValue) 662 writeElement( pCoreProps, FSNS( XML_cp, XML_contentStatus ), aValue ); 663 } 664 665 it = aUserDefinedProperties.find("OOXMLCorePropertyContentType"); 666 if (it != aUserDefinedProperties.end()) 667 { 668 OUString aValue; 669 if (it->second >>= aValue) 670 writeElement( pCoreProps, FSNS( XML_cp, XML_contentType ), aValue ); 671 } 672 writeElement( pCoreProps, FSNS( XML_dcterms, XML_created ), xProperties->getCreationDate() ); 673 writeElement( pCoreProps, FSNS( XML_dc, XML_creator ), xProperties->getAuthor() ); 674 writeElement( pCoreProps, FSNS( XML_dc, XML_description ), xProperties->getDescription() ); 675 676 it = aUserDefinedProperties.find("OOXMLCorePropertyIdentifier"); 677 if (it != aUserDefinedProperties.end()) 678 { 679 OUString aValue; 680 if (it->second >>= aValue) 681 writeElement( pCoreProps, FSNS( XML_dc, XML_identifier ), aValue ); 682 } 683 writeElement( pCoreProps, FSNS( XML_cp, XML_keywords ), xProperties->getKeywords() ); 684 writeElement( pCoreProps, FSNS( XML_dc, XML_language ), LanguageTag( xProperties->getLanguage()) ); 685 writeElement( pCoreProps, FSNS( XML_cp, XML_lastModifiedBy ), xProperties->getModifiedBy() ); 686 writeElement( pCoreProps, FSNS( XML_cp, XML_lastPrinted ), xProperties->getPrintDate() ); 687 writeElement( pCoreProps, FSNS( XML_dcterms, XML_modified ), xProperties->getModificationDate() ); 688 writeElement( pCoreProps, FSNS( XML_cp, XML_revision ), xProperties->getEditingCycles() ); 689 writeElement( pCoreProps, FSNS( XML_dc, XML_subject ), xProperties->getSubject() ); 690 writeElement( pCoreProps, FSNS( XML_dc, XML_title ), xProperties->getTitle() ); 691 692 it = aUserDefinedProperties.find("OOXMLCorePropertyVersion"); 693 if (it != aUserDefinedProperties.end()) 694 { 695 OUString aValue; 696 if (it->second >>= aValue) 697 writeElement( pCoreProps, FSNS( XML_cp, XML_version ), aValue ); 698 } 699 700 pCoreProps->endElementNS( XML_cp, XML_coreProperties ); 701 } 702 703 static void 704 writeAppProperties( XmlFilterBase& rSelf, const Reference< XDocumentProperties >& xProperties ) 705 { 706 rSelf.addRelation( 707 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties", 708 "docProps/app.xml" ); 709 FSHelperPtr pAppProps = rSelf.openFragmentStreamWithSerializer( 710 "docProps/app.xml", 711 "application/vnd.openxmlformats-officedocument.extended-properties+xml" ); 712 pAppProps->startElement( XML_Properties, 713 XML_xmlns, rSelf.getNamespaceURL(OOX_NS(officeExtPr)), 714 FSNS(XML_xmlns, XML_vt), rSelf.getNamespaceURL(OOX_NS(officeDocPropsVT))); 715 716 uno::Reference<beans::XPropertyAccess> xUserDefinedProperties(xProperties->getUserDefinedProperties(), uno::UNO_QUERY); 717 comphelper::SequenceAsHashMap aUserDefinedProperties(xUserDefinedProperties->getPropertyValues()); 718 comphelper::SequenceAsHashMap::iterator it; 719 720 writeElement( pAppProps, XML_Template, xProperties->getTemplateName() ); 721 722 it = aUserDefinedProperties.find("Manager"); 723 if (it != aUserDefinedProperties.end()) 724 { 725 OUString aValue; 726 if (it->second >>= aValue) 727 writeElement( pAppProps, XML_Manager, aValue ); 728 } 729 730 #ifdef OOXTODO 731 writeElement( pAppProps, XML_PresentationFormat, "presentation format" ); 732 writeElement( pAppProps, XML_Lines, "lines" ); 733 writeElement( pAppProps, XML_Slides, "slides" ); 734 writeElement( pAppProps, XML_Notes, "notes" ); 735 #endif /* def OOXTODO */ 736 // EditingDuration is in seconds, TotalTime is in minutes. 737 writeElement( pAppProps, XML_TotalTime, xProperties->getEditingDuration() / 60 ); 738 #ifdef OOXTODO 739 writeElement( pAppProps, XML_HiddenSlides, "hidden slides" ); 740 writeElement( pAppProps, XML_MMClips, "mm clips" ); 741 writeElement( pAppProps, XML_ScaleCrop, "scale crop" ); 742 writeElement( pAppProps, XML_HeadingPairs, "heading pairs" ); 743 writeElement( pAppProps, XML_TitlesOfParts, "titles of parts" ); 744 writeElement( pAppProps, XML_LinksUpToDate, "links up-to-date" ); 745 writeElement( pAppProps, XML_SharedDoc, "shared doc" ); 746 writeElement( pAppProps, XML_HLinks, "hlinks" ); 747 writeElement( pAppProps, XML_HyperlinksChanged, "hyperlinks changed" ); 748 writeElement( pAppProps, XML_DigSig, "digital signature" ); 749 #endif /* def OOXTODO */ 750 writeElement( pAppProps, XML_Application, utl::DocInfoHelper::GetGeneratorString() ); 751 752 it = aUserDefinedProperties.find("HyperlinkBase"); 753 if (it != aUserDefinedProperties.end()) 754 { 755 OUString aValue; 756 if (it->second >>= aValue) 757 writeElement( pAppProps, XML_HyperlinkBase, aValue ); 758 } 759 // AppVersion specifies the version of the application which produced document 760 // It is strictly connected with MS Office versions: 761 // * 12: [Office 2007] [LO < 7.0] 762 // * 14: [Office 2010] 763 // * 15: [Office 2013/2016/2019] [LO >= 7.0] 764 // The LibreOffice is application on 2013/2016/2019 level 765 writeElement( pAppProps, XML_AppVersion, u"15.0000" ); 766 767 // OOXTODO Calculate DocSecurity value based on security (password, read-only etc.) 768 it = aUserDefinedProperties.find("DocSecurity"); 769 if (it != aUserDefinedProperties.end()) 770 { 771 sal_Int32 nValue; 772 if (it->second >>= nValue) 773 writeElement( pAppProps, XML_DocSecurity, nValue ); 774 } 775 776 comphelper::SequenceAsHashMap aStats = xProperties->getDocumentStatistics(); 777 sal_Int32 nValue = 0; 778 779 it = aStats.find("PageCount"); 780 if (it != aStats.end()) 781 { 782 if (it->second >>= nValue) 783 writeElement(pAppProps, XML_Pages, nValue); 784 } 785 786 it = aStats.find("WordCount"); 787 if (it != aStats.end()) 788 { 789 if (it->second >>= nValue) 790 writeElement(pAppProps, XML_Words, nValue); 791 } 792 793 it = aStats.find("NonWhitespaceCharacterCount"); 794 if (it != aStats.end()) 795 { 796 if (it->second >>= nValue) 797 writeElement(pAppProps, XML_Characters, nValue); 798 } 799 800 it = aStats.find("CharacterCount"); 801 if (it != aStats.end()) 802 { 803 if (it->second >>= nValue) 804 writeElement(pAppProps, XML_CharactersWithSpaces, nValue); 805 } 806 807 it = aStats.find("ParagraphCount"); 808 if (it != aStats.end()) 809 { 810 if (it->second >>= nValue) 811 writeElement(pAppProps, XML_Paragraphs, nValue); 812 } 813 814 it = aUserDefinedProperties.find("Company"); 815 if (it != aUserDefinedProperties.end()) 816 { 817 OUString aValue; 818 if (it->second >>= aValue) 819 writeElement(pAppProps, XML_Company, aValue); 820 } 821 822 pAppProps->endElement( XML_Properties ); 823 } 824 825 static void 826 writeCustomProperties( XmlFilterBase& rSelf, const Reference< XDocumentProperties >& xProperties, bool bSecurityOptOpenReadOnly ) 827 { 828 uno::Reference<beans::XPropertyAccess> xUserDefinedProperties( xProperties->getUserDefinedProperties(), uno::UNO_QUERY ); 829 auto aprop = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(xUserDefinedProperties->getPropertyValues()); 830 sal_Int32 nbCustomProperties = aprop.size(); 831 // tdf#89791 : if no custom properties, no need to add docProps/custom.x 832 // tdf#107690: except the case of read-only documents, because that 833 // is handled by the _MarkAsFinal custom property in MSO. 834 if (!nbCustomProperties && !bSecurityOptOpenReadOnly) 835 return; 836 837 if (bSecurityOptOpenReadOnly) 838 { 839 PropertyValue aPropertyValue; 840 // MSO custom property for read-only documents 841 aPropertyValue.Name = "_MarkAsFinal"; 842 aPropertyValue.Value <<= true; 843 aprop.push_back(aPropertyValue); 844 } 845 846 rSelf.addRelation( 847 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties", 848 "docProps/custom.xml" ); 849 FSHelperPtr pAppProps = rSelf.openFragmentStreamWithSerializer( 850 "docProps/custom.xml", 851 "application/vnd.openxmlformats-officedocument.custom-properties+xml" ); 852 pAppProps->startElement( XML_Properties, 853 XML_xmlns, rSelf.getNamespaceURL(OOX_NS(officeCustomPr)), 854 FSNS(XML_xmlns, XML_vt), rSelf.getNamespaceURL(OOX_NS(officeDocPropsVT))); 855 856 size_t nIndex = 0; 857 for (const auto& rProp : aprop) 858 { 859 if ( !rProp.Name.isEmpty() ) 860 { 861 OString aName = OUStringToOString( rProp.Name, RTL_TEXTENCODING_ASCII_US ); 862 // Skip storing these values in Custom Properties as it will be stored in Core/Extended Properties 863 if (( aName == "OOXMLCorePropertyCategory" ) || // stored in cp:category 864 ( aName == "OOXMLCorePropertyContentStatus" ) || // stored in cp:contentStatus 865 ( aName == "OOXMLCorePropertyContentType" ) || // stored in cp:contentType 866 ( aName == "OOXMLCorePropertyIdentifier" ) || // stored in dc:identifier 867 ( aName == "OOXMLCorePropertyVersion" ) || // stored in cp:version 868 ( aName == "HyperlinkBase" ) || // stored in Extended File Properties 869 ( aName == "AppVersion" ) || // stored in Extended File Properties 870 ( aName == "DocSecurity" ) || // stored in Extended File Properties 871 ( aName == "Manager" ) || // stored in Extended File Properties 872 ( aName == "Company" )) // stored in Extended File Properties 873 continue; 874 875 // pid starts from 2 not from 1 as MS supports pid from 2 876 pAppProps->startElement( XML_property , 877 XML_fmtid, "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}", 878 XML_pid, OString::number(nIndex + 2), 879 XML_name, aName); 880 881 switch ( rProp.Value.getValueTypeClass() ) 882 { 883 case TypeClass_STRING: 884 { 885 OUString aValue; 886 rProp.Value >>= aValue; 887 writeElement( pAppProps, FSNS( XML_vt, XML_lpwstr ), aValue ); 888 } 889 break; 890 case TypeClass_BOOLEAN: 891 { 892 bool val = *o3tl::forceAccess<bool>(rProp.Value); 893 writeElement( pAppProps, FSNS( XML_vt, XML_bool ), val ? 1 : 0); 894 } 895 break; 896 case TypeClass_DOUBLE: 897 { 898 double num = {}; // spurious -Werror=maybe-uninitialized 899 if ( rProp.Value >>= num ) 900 { 901 // r8 - 8-byte real number 902 writeElement( pAppProps, FSNS( XML_vt, XML_r8 ), OUString::number(num) ); 903 } 904 } 905 break; 906 default: 907 { 908 double num = {}; // spurious -Werror=maybe-uninitialized 909 util::Date aDate; 910 util::Duration aDuration; 911 util::DateTime aDateTime; 912 if ( rProp.Value >>= num ) 913 { 914 // i4 - 4-byte signed integer 915 writeElement( pAppProps, FSNS( XML_vt, XML_i4 ), num ); 916 } 917 else if ( rProp.Value >>= aDate ) 918 { 919 aDateTime = util::DateTime( 0, 0 , 0, 0, aDate.Day, aDate.Month, aDate.Year, true ); 920 writeElement( pAppProps, FSNS( XML_vt, XML_filetime ), aDateTime); 921 } 922 else if ( rProp.Value >>= aDuration ) 923 { 924 OUStringBuffer buf; 925 ::sax::Converter::convertDuration( buf, aDuration ); 926 OUString aDurationStr = buf.makeStringAndClear(); 927 writeElement( pAppProps, FSNS( XML_vt, XML_lpwstr ), aDurationStr ); 928 } 929 else if ( rProp.Value >>= aDateTime ) 930 writeElement( pAppProps, FSNS( XML_vt, XML_filetime ), aDateTime ); 931 else 932 //no other options 933 OSL_FAIL( "XMLFilterBase::writeCustomProperties unsupported value type!" ); 934 } 935 break; 936 } 937 pAppProps->endElement( XML_property ); 938 } 939 ++nIndex; 940 } 941 pAppProps->endElement( XML_Properties ); 942 } 943 944 void XmlFilterBase::exportDocumentProperties( const Reference< XDocumentProperties >& xProperties, bool bSecurityOptOpenReadOnly ) 945 { 946 if( xProperties.is() ) 947 { 948 writeCoreProperties( *this, xProperties ); 949 writeAppProperties( *this, xProperties ); 950 writeCustomProperties( *this, xProperties, bSecurityOptOpenReadOnly ); 951 } 952 } 953 954 // protected ------------------------------------------------------------------ 955 956 Reference< XInputStream > XmlFilterBase::implGetInputStream( MediaDescriptor& rMediaDesc ) const 957 { 958 /* Get the input stream directly from the media descriptor, or decrypt the 959 package again. The latter is needed e.g. when the document is reloaded. 960 All this is implemented in the detector service. */ 961 rtl::Reference< FilterDetect > xDetector( new FilterDetect( getComponentContext() ) ); 962 return xDetector->extractUnencryptedPackage( rMediaDesc ); 963 } 964 965 Reference<XStream> XmlFilterBase::implGetOutputStream( MediaDescriptor& rMediaDescriptor ) const 966 { 967 const Sequence< NamedValue > aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault( 968 MediaDescriptor::PROP_ENCRYPTIONDATA(), 969 Sequence< NamedValue >() ); 970 971 if (aMediaEncData.getLength() == 0) 972 { 973 return FilterBase::implGetOutputStream( rMediaDescriptor ); 974 } 975 else // We need to encrypt the stream so create a memory stream 976 { 977 Reference< XComponentContext > xContext = getComponentContext(); 978 return Reference< XStream > ( 979 xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.MemoryStream", xContext), 980 uno::UNO_QUERY_THROW ); 981 } 982 } 983 984 bool XmlFilterBase::implFinalizeExport( MediaDescriptor& rMediaDescriptor ) 985 { 986 bool bRet = true; 987 988 const Sequence< NamedValue > aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault( 989 MediaDescriptor::PROP_ENCRYPTIONDATA(), 990 Sequence< NamedValue >() ); 991 992 if (aMediaEncData.getLength()) 993 { 994 commitStorage(); 995 996 Reference< XStream> xDocumentStream (FilterBase::implGetOutputStream(rMediaDescriptor)); 997 oox::ole::OleStorage aOleStorage( getComponentContext(), xDocumentStream, true ); 998 crypto::DocumentEncryption encryptor( getComponentContext(), getMainDocumentStream(), aOleStorage, aMediaEncData ); 999 bRet = encryptor.encrypt(); 1000 if (bRet) 1001 aOleStorage.commit(); 1002 } 1003 1004 return bRet; 1005 } 1006 1007 // private -------------------------------------------------------------------- 1008 1009 StorageRef XmlFilterBase::implCreateStorage( const Reference< XInputStream >& rxInStream ) const 1010 { 1011 return std::make_shared<ZipStorage>( getComponentContext(), rxInStream ); 1012 } 1013 1014 StorageRef XmlFilterBase::implCreateStorage( const Reference< XStream >& rxOutStream ) const 1015 { 1016 return std::make_shared<ZipStorage>( getComponentContext(), rxOutStream ); 1017 } 1018 1019 bool XmlFilterBase::isMSO2007Document() const 1020 { 1021 return mbMSO2007; 1022 } 1023 1024 void XmlFilterBase::setMissingExtDrawing() 1025 { 1026 mbMissingExtDrawing = true; 1027 } 1028 1029 void XmlFilterBase::setDiagramFontHeights(NamedShapePairs* pDiagramFontHeights) 1030 { 1031 mxImpl->mpDiagramFontHeights = pDiagramFontHeights; 1032 } 1033 1034 NamedShapePairs* XmlFilterBase::getDiagramFontHeights() { return mxImpl->mpDiagramFontHeights; } 1035 1036 OUString XmlFilterBase::getNamespaceURL(sal_Int32 nNSID) const 1037 { 1038 auto itr = mxImpl->mrNamespaceMap.maTransitionalNamespaceMap.find(nNSID); 1039 if (itr == mxImpl->mrNamespaceMap.maTransitionalNamespaceMap.end()) 1040 { 1041 SAL_WARN("oox", "missing namespace in the namespace map for : " << nNSID); 1042 return OUString(); 1043 } 1044 1045 return itr->second; 1046 } 1047 1048 void XmlFilterBase::importCustomFragments(css::uno::Reference<css::embed::XStorage> const & xDocumentStorage) 1049 { 1050 Reference<XRelationshipAccess> xRelations(xDocumentStorage, UNO_QUERY); 1051 if (!xRelations.is()) 1052 return; 1053 1054 const uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships(); 1055 1056 std::vector<StreamDataSequence> aCustomFragments; 1057 std::vector<OUString> aCustomFragmentTypes; 1058 std::vector<OUString> aCustomFragmentTargets; 1059 for (const uno::Sequence<beans::StringPair>& aSeq : aSeqs) 1060 { 1061 OUString sType; 1062 OUString sTarget; 1063 for (const beans::StringPair& aPair : aSeq) 1064 { 1065 if (aPair.First == "Target") 1066 sTarget = aPair.Second; 1067 else if (aPair.First == "Type") 1068 sType = aPair.Second; 1069 } 1070 1071 // Preserve non-standard (i.e. custom) entries. 1072 if (!sType.match("http://schemas.openxmlformats.org") // OOXML/ECMA Transitional 1073 && !sType.match("http://purl.oclc.org")) // OOXML Strict 1074 { 1075 StreamDataSequence aDataSeq; 1076 if (importBinaryData(aDataSeq, sTarget)) 1077 { 1078 aCustomFragments.emplace_back(aDataSeq); 1079 aCustomFragmentTypes.emplace_back(sType); 1080 aCustomFragmentTargets.emplace_back(sTarget); 1081 } 1082 } 1083 } 1084 1085 // Adding the saved custom xml DOM 1086 comphelper::SequenceAsHashMap aGrabBagProperties; 1087 aGrabBagProperties["OOXCustomFragments"] <<= comphelper::containerToSequence(aCustomFragments); 1088 aGrabBagProperties["OOXCustomFragmentTypes"] <<= comphelper::containerToSequence(aCustomFragmentTypes); 1089 aGrabBagProperties["OOXCustomFragmentTargets"] <<= comphelper::containerToSequence(aCustomFragmentTargets); 1090 1091 std::vector<uno::Reference<xml::dom::XDocument>> aCustomXmlDomList; 1092 std::vector<uno::Reference<xml::dom::XDocument>> aCustomXmlDomPropsList; 1093 //FIXME: Ideally, we should get these the relations, but it seems that is not consistently set. 1094 // In some cases it's stored in the workbook relationships, which is unexpected. So we discover them directly. 1095 for (int i = 1; ; ++i) 1096 { 1097 Reference<XDocument> xCustDoc = importFragment("customXml/item" + OUString::number(i) + ".xml"); 1098 Reference<XDocument> xCustDocProps = importFragment("customXml/itemProps" + OUString::number(i) + ".xml"); 1099 if (xCustDoc && xCustDocProps) 1100 { 1101 aCustomXmlDomList.emplace_back(xCustDoc); 1102 aCustomXmlDomPropsList.emplace_back(xCustDocProps); 1103 } 1104 else 1105 break; 1106 } 1107 1108 // Adding the saved custom xml DOM 1109 aGrabBagProperties["OOXCustomXml"] <<= comphelper::containerToSequence(aCustomXmlDomList); 1110 aGrabBagProperties["OOXCustomXmlProps"] <<= comphelper::containerToSequence(aCustomXmlDomPropsList); 1111 1112 // Save the [Content_Types].xml after parsing. 1113 uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypeInfo; 1114 uno::Reference<io::XInputStream> xInputStream = openInputStream("[Content_Types].xml"); 1115 if (xInputStream.is()) 1116 aContentTypeInfo = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xInputStream, getComponentContext()); 1117 1118 aGrabBagProperties["OOXContentTypes"] <<= aContentTypeInfo; 1119 1120 Reference<XComponent> xModel = getModel(); 1121 oox::core::XmlFilterBase::putPropertiesToDocumentGrabBag(xModel, aGrabBagProperties); 1122 } 1123 1124 void XmlFilterBase::exportCustomFragments() 1125 { 1126 Reference<XComponent> xModel = getModel(); 1127 uno::Reference<beans::XPropertySet> xPropSet(xModel, uno::UNO_QUERY_THROW); 1128 1129 uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo(); 1130 static constexpr OUStringLiteral aName = u"" UNO_NAME_MISC_OBJ_INTEROPGRABBAG; 1131 if (!xPropSetInfo->hasPropertyByName(aName)) 1132 return; 1133 1134 uno::Sequence<uno::Reference<xml::dom::XDocument>> customXmlDomlist; 1135 uno::Sequence<uno::Reference<xml::dom::XDocument>> customXmlDomPropslist; 1136 uno::Sequence<StreamDataSequence> customFragments; 1137 uno::Sequence<OUString> customFragmentTypes; 1138 uno::Sequence<OUString> customFragmentTargets; 1139 uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypes; 1140 1141 uno::Sequence<beans::PropertyValue> propList; 1142 xPropSet->getPropertyValue(aName) >>= propList; 1143 for (const auto& rProp : std::as_const(propList)) 1144 { 1145 const OUString propName = rProp.Name; 1146 if (propName == "OOXCustomXml") 1147 { 1148 rProp.Value >>= customXmlDomlist; 1149 } 1150 else if (propName == "OOXCustomXmlProps") 1151 { 1152 rProp.Value >>= customXmlDomPropslist; 1153 } 1154 else if (propName == "OOXCustomFragments") 1155 { 1156 rProp.Value >>= customFragments; 1157 } 1158 else if (propName == "OOXCustomFragmentTypes") 1159 { 1160 rProp.Value >>= customFragmentTypes; 1161 } 1162 else if (propName == "OOXCustomFragmentTargets") 1163 { 1164 rProp.Value >>= customFragmentTargets; 1165 } 1166 else if (propName == "OOXContentTypes") 1167 { 1168 rProp.Value >>= aContentTypes; 1169 } 1170 } 1171 1172 // Expect customXmlDomPropslist.getLength() == customXmlDomlist.getLength(). 1173 for (sal_Int32 j = 0; j < customXmlDomlist.getLength(); j++) 1174 { 1175 uno::Reference<xml::dom::XDocument> customXmlDom = customXmlDomlist[j]; 1176 uno::Reference<xml::dom::XDocument> customXmlDomProps = customXmlDomPropslist[j]; 1177 const OUString fragmentPath = "customXml/item" + OUString::number((j+1)) + ".xml"; 1178 if (customXmlDom.is()) 1179 { 1180 addRelation(oox::getRelationship(Relationship::CUSTOMXML), "../" + fragmentPath); 1181 1182 uno::Reference<xml::sax::XSAXSerializable> serializer(customXmlDom, uno::UNO_QUERY); 1183 uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext()); 1184 writer->setOutputStream(openFragmentStream(fragmentPath, "application/xml")); 1185 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), 1186 uno::Sequence<beans::StringPair>()); 1187 } 1188 1189 if (customXmlDomProps.is()) 1190 { 1191 uno::Reference<xml::sax::XSAXSerializable> serializer(customXmlDomProps, uno::UNO_QUERY); 1192 uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext()); 1193 writer->setOutputStream(openFragmentStream("customXml/itemProps"+OUString::number((j+1))+".xml", 1194 "application/vnd.openxmlformats-officedocument.customXmlProperties+xml")); 1195 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), 1196 uno::Sequence<beans::StringPair>()); 1197 1198 // Adding itemprops's relationship entry to item.xml.rels file 1199 addRelation(openFragmentStream(fragmentPath, "application/xml"), 1200 oox::getRelationship(Relationship::CUSTOMXMLPROPS), 1201 "itemProps"+OUString::number((j+1))+".xml"); 1202 } 1203 } 1204 1205 // Expect customFragments.getLength() == customFragmentTypes.getLength() == customFragmentTargets.getLength(). 1206 for (sal_Int32 j = 0; j < customFragments.getLength(); j++) 1207 { 1208 addRelation(customFragmentTypes[j], customFragmentTargets[j]); 1209 const OUString aFilename = customFragmentTargets[j]; 1210 Reference<XOutputStream> xOutStream = openOutputStream(aFilename); 1211 if (xOutStream.is()) 1212 { 1213 xOutStream->writeBytes(customFragments[j]); 1214 uno::Reference<XPropertySet> xProps(xOutStream, uno::UNO_QUERY); 1215 if (xProps.is()) 1216 { 1217 const OUString aType = comphelper::OFOPXMLHelper::GetContentTypeByName(aContentTypes, aFilename); 1218 const OUString aContentType = (aType.getLength() ? aType : OUString("application/octet-stream")); 1219 xProps->setPropertyValue("MediaType", uno::makeAny(aContentType)); 1220 } 1221 } 1222 } 1223 } 1224 1225 } // namespace oox::core 1226 1227 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1228
