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 OUStringToOString( aFragmentPath, RTL_TEXTENCODING_ASCII_US ) + "'" ).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, std::u16string_view 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, std::u16string_view 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, std::u16string_view 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( " " + 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, u"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 u"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 u"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 // tdf#127864 - export custom document properties using utf8 text encoding 862 OString aName = OUStringToOString(rProp.Name, RTL_TEXTENCODING_UTF8); 863 // Skip storing these values in Custom Properties as it will be stored in Core/Extended Properties 864 if (( aName == "OOXMLCorePropertyCategory" ) || // stored in cp:category 865 ( aName == "OOXMLCorePropertyContentStatus" ) || // stored in cp:contentStatus 866 ( aName == "OOXMLCorePropertyContentType" ) || // stored in cp:contentType 867 ( aName == "OOXMLCorePropertyIdentifier" ) || // stored in dc:identifier 868 ( aName == "OOXMLCorePropertyVersion" ) || // stored in cp:version 869 ( aName == "HyperlinkBase" ) || // stored in Extended File Properties 870 ( aName == "AppVersion" ) || // stored in Extended File Properties 871 ( aName == "DocSecurity" ) || // stored in Extended File Properties 872 ( aName == "Manager" ) || // stored in Extended File Properties 873 ( aName == "Company" )) // stored in Extended File Properties 874 continue; 875 876 // pid starts from 2 not from 1 as MS supports pid from 2 877 pAppProps->startElement( XML_property , 878 XML_fmtid, "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}", 879 XML_pid, OString::number(nIndex + 2), 880 XML_name, aName); 881 882 switch ( rProp.Value.getValueTypeClass() ) 883 { 884 case TypeClass_STRING: 885 { 886 OUString aValue; 887 rProp.Value >>= aValue; 888 writeElement( pAppProps, FSNS( XML_vt, XML_lpwstr ), aValue ); 889 } 890 break; 891 case TypeClass_BOOLEAN: 892 { 893 bool val = *o3tl::forceAccess<bool>(rProp.Value); 894 writeElement( pAppProps, FSNS( XML_vt, XML_bool ), val ? 1 : 0); 895 } 896 break; 897 case TypeClass_DOUBLE: 898 { 899 double num = {}; // spurious -Werror=maybe-uninitialized 900 if ( rProp.Value >>= num ) 901 { 902 // r8 - 8-byte real number 903 writeElement( pAppProps, FSNS( XML_vt, XML_r8 ), OUString::number(num) ); 904 } 905 } 906 break; 907 default: 908 { 909 double num = {}; // spurious -Werror=maybe-uninitialized 910 util::Date aDate; 911 util::Duration aDuration; 912 util::DateTime aDateTime; 913 if ( rProp.Value >>= num ) 914 { 915 // i4 - 4-byte signed integer 916 writeElement( pAppProps, FSNS( XML_vt, XML_i4 ), num ); 917 } 918 else if ( rProp.Value >>= aDate ) 919 { 920 aDateTime = util::DateTime( 0, 0 , 0, 0, aDate.Day, aDate.Month, aDate.Year, true ); 921 writeElement( pAppProps, FSNS( XML_vt, XML_filetime ), aDateTime); 922 } 923 else if ( rProp.Value >>= aDuration ) 924 { 925 OUStringBuffer buf; 926 ::sax::Converter::convertDuration( buf, aDuration ); 927 OUString aDurationStr = buf.makeStringAndClear(); 928 writeElement( pAppProps, FSNS( XML_vt, XML_lpwstr ), aDurationStr ); 929 } 930 else if ( rProp.Value >>= aDateTime ) 931 writeElement( pAppProps, FSNS( XML_vt, XML_filetime ), aDateTime ); 932 else 933 //no other options 934 OSL_FAIL( "XMLFilterBase::writeCustomProperties unsupported value type!" ); 935 } 936 break; 937 } 938 pAppProps->endElement( XML_property ); 939 } 940 ++nIndex; 941 } 942 pAppProps->endElement( XML_Properties ); 943 } 944 945 void XmlFilterBase::exportDocumentProperties( const Reference< XDocumentProperties >& xProperties, bool bSecurityOptOpenReadOnly ) 946 { 947 if( xProperties.is() ) 948 { 949 writeCoreProperties( *this, xProperties ); 950 writeAppProperties( *this, xProperties ); 951 writeCustomProperties( *this, xProperties, bSecurityOptOpenReadOnly ); 952 } 953 } 954 955 // protected ------------------------------------------------------------------ 956 957 Reference< XInputStream > XmlFilterBase::implGetInputStream( MediaDescriptor& rMediaDesc ) const 958 { 959 /* Get the input stream directly from the media descriptor, or decrypt the 960 package again. The latter is needed e.g. when the document is reloaded. 961 All this is implemented in the detector service. */ 962 rtl::Reference< FilterDetect > xDetector( new FilterDetect( getComponentContext() ) ); 963 return xDetector->extractUnencryptedPackage( rMediaDesc ); 964 } 965 966 Reference<XStream> XmlFilterBase::implGetOutputStream( MediaDescriptor& rMediaDescriptor ) const 967 { 968 const Sequence< NamedValue > aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault( 969 MediaDescriptor::PROP_ENCRYPTIONDATA(), 970 Sequence< NamedValue >() ); 971 972 if (aMediaEncData.getLength() == 0) 973 { 974 return FilterBase::implGetOutputStream( rMediaDescriptor ); 975 } 976 else // We need to encrypt the stream so create a memory stream 977 { 978 Reference< XComponentContext > xContext = getComponentContext(); 979 return Reference< XStream > ( 980 xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.MemoryStream", xContext), 981 uno::UNO_QUERY_THROW ); 982 } 983 } 984 985 bool XmlFilterBase::implFinalizeExport( MediaDescriptor& rMediaDescriptor ) 986 { 987 bool bRet = true; 988 989 const Sequence< NamedValue > aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault( 990 MediaDescriptor::PROP_ENCRYPTIONDATA(), 991 Sequence< NamedValue >() ); 992 993 if (aMediaEncData.getLength()) 994 { 995 commitStorage(); 996 997 Reference< XStream> xDocumentStream (FilterBase::implGetOutputStream(rMediaDescriptor)); 998 oox::ole::OleStorage aOleStorage( getComponentContext(), xDocumentStream, true ); 999 crypto::DocumentEncryption encryptor( getComponentContext(), getMainDocumentStream(), aOleStorage, aMediaEncData ); 1000 bRet = encryptor.encrypt(); 1001 if (bRet) 1002 aOleStorage.commit(); 1003 } 1004 1005 return bRet; 1006 } 1007 1008 // private -------------------------------------------------------------------- 1009 1010 StorageRef XmlFilterBase::implCreateStorage( const Reference< XInputStream >& rxInStream ) const 1011 { 1012 return std::make_shared<ZipStorage>( getComponentContext(), rxInStream ); 1013 } 1014 1015 StorageRef XmlFilterBase::implCreateStorage( const Reference< XStream >& rxOutStream ) const 1016 { 1017 return std::make_shared<ZipStorage>( getComponentContext(), rxOutStream ); 1018 } 1019 1020 bool XmlFilterBase::isMSO2007Document() const 1021 { 1022 return mbMSO2007; 1023 } 1024 1025 void XmlFilterBase::setMissingExtDrawing() 1026 { 1027 mbMissingExtDrawing = true; 1028 } 1029 1030 void XmlFilterBase::setDiagramFontHeights(NamedShapePairs* pDiagramFontHeights) 1031 { 1032 mxImpl->mpDiagramFontHeights = pDiagramFontHeights; 1033 } 1034 1035 NamedShapePairs* XmlFilterBase::getDiagramFontHeights() { return mxImpl->mpDiagramFontHeights; } 1036 1037 OUString XmlFilterBase::getNamespaceURL(sal_Int32 nNSID) const 1038 { 1039 auto itr = mxImpl->mrNamespaceMap.maTransitionalNamespaceMap.find(nNSID); 1040 if (itr == mxImpl->mrNamespaceMap.maTransitionalNamespaceMap.end()) 1041 { 1042 SAL_WARN("oox", "missing namespace in the namespace map for : " << nNSID); 1043 return OUString(); 1044 } 1045 1046 return itr->second; 1047 } 1048 1049 void XmlFilterBase::importCustomFragments(css::uno::Reference<css::embed::XStorage> const & xDocumentStorage) 1050 { 1051 Reference<XRelationshipAccess> xRelations(xDocumentStorage, UNO_QUERY); 1052 if (!xRelations.is()) 1053 return; 1054 1055 const uno::Sequence<uno::Sequence<beans::StringPair>> aSeqs = xRelations->getAllRelationships(); 1056 1057 std::vector<StreamDataSequence> aCustomFragments; 1058 std::vector<OUString> aCustomFragmentTypes; 1059 std::vector<OUString> aCustomFragmentTargets; 1060 for (const uno::Sequence<beans::StringPair>& aSeq : aSeqs) 1061 { 1062 OUString sType; 1063 OUString sTarget; 1064 for (const beans::StringPair& aPair : aSeq) 1065 { 1066 if (aPair.First == "Target") 1067 sTarget = aPair.Second; 1068 else if (aPair.First == "Type") 1069 sType = aPair.Second; 1070 } 1071 1072 // Preserve non-standard (i.e. custom) entries. 1073 if (!sType.match("http://schemas.openxmlformats.org") // OOXML/ECMA Transitional 1074 && !sType.match("http://purl.oclc.org")) // OOXML Strict 1075 { 1076 StreamDataSequence aDataSeq; 1077 if (importBinaryData(aDataSeq, sTarget)) 1078 { 1079 aCustomFragments.emplace_back(aDataSeq); 1080 aCustomFragmentTypes.emplace_back(sType); 1081 aCustomFragmentTargets.emplace_back(sTarget); 1082 } 1083 } 1084 } 1085 1086 // Adding the saved custom xml DOM 1087 comphelper::SequenceAsHashMap aGrabBagProperties; 1088 aGrabBagProperties["OOXCustomFragments"] <<= comphelper::containerToSequence(aCustomFragments); 1089 aGrabBagProperties["OOXCustomFragmentTypes"] <<= comphelper::containerToSequence(aCustomFragmentTypes); 1090 aGrabBagProperties["OOXCustomFragmentTargets"] <<= comphelper::containerToSequence(aCustomFragmentTargets); 1091 1092 std::vector<uno::Reference<xml::dom::XDocument>> aCustomXmlDomList; 1093 std::vector<uno::Reference<xml::dom::XDocument>> aCustomXmlDomPropsList; 1094 //FIXME: Ideally, we should get these the relations, but it seems that is not consistently set. 1095 // In some cases it's stored in the workbook relationships, which is unexpected. So we discover them directly. 1096 for (int i = 1; ; ++i) 1097 { 1098 Reference<XDocument> xCustDoc = importFragment("customXml/item" + OUString::number(i) + ".xml"); 1099 Reference<XDocument> xCustDocProps = importFragment("customXml/itemProps" + OUString::number(i) + ".xml"); 1100 if (xCustDoc && xCustDocProps) 1101 { 1102 aCustomXmlDomList.emplace_back(xCustDoc); 1103 aCustomXmlDomPropsList.emplace_back(xCustDocProps); 1104 } 1105 else 1106 break; 1107 } 1108 1109 // Adding the saved custom xml DOM 1110 aGrabBagProperties["OOXCustomXml"] <<= comphelper::containerToSequence(aCustomXmlDomList); 1111 aGrabBagProperties["OOXCustomXmlProps"] <<= comphelper::containerToSequence(aCustomXmlDomPropsList); 1112 1113 // Save the [Content_Types].xml after parsing. 1114 uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypeInfo; 1115 uno::Reference<io::XInputStream> xInputStream = openInputStream("[Content_Types].xml"); 1116 if (xInputStream.is()) 1117 aContentTypeInfo = comphelper::OFOPXMLHelper::ReadContentTypeSequence(xInputStream, getComponentContext()); 1118 1119 aGrabBagProperties["OOXContentTypes"] <<= aContentTypeInfo; 1120 1121 Reference<XComponent> xModel = getModel(); 1122 oox::core::XmlFilterBase::putPropertiesToDocumentGrabBag(xModel, aGrabBagProperties); 1123 } 1124 1125 void XmlFilterBase::exportCustomFragments() 1126 { 1127 Reference<XComponent> xModel = getModel(); 1128 uno::Reference<beans::XPropertySet> xPropSet(xModel, uno::UNO_QUERY_THROW); 1129 1130 uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo(); 1131 static constexpr OUStringLiteral aName = u"" UNO_NAME_MISC_OBJ_INTEROPGRABBAG; 1132 if (!xPropSetInfo->hasPropertyByName(aName)) 1133 return; 1134 1135 uno::Sequence<uno::Reference<xml::dom::XDocument>> customXmlDomlist; 1136 uno::Sequence<uno::Reference<xml::dom::XDocument>> customXmlDomPropslist; 1137 uno::Sequence<StreamDataSequence> customFragments; 1138 uno::Sequence<OUString> customFragmentTypes; 1139 uno::Sequence<OUString> customFragmentTargets; 1140 uno::Sequence<uno::Sequence<beans::StringPair>> aContentTypes; 1141 1142 uno::Sequence<beans::PropertyValue> propList; 1143 xPropSet->getPropertyValue(aName) >>= propList; 1144 for (const auto& rProp : std::as_const(propList)) 1145 { 1146 const OUString propName = rProp.Name; 1147 if (propName == "OOXCustomXml") 1148 { 1149 rProp.Value >>= customXmlDomlist; 1150 } 1151 else if (propName == "OOXCustomXmlProps") 1152 { 1153 rProp.Value >>= customXmlDomPropslist; 1154 } 1155 else if (propName == "OOXCustomFragments") 1156 { 1157 rProp.Value >>= customFragments; 1158 } 1159 else if (propName == "OOXCustomFragmentTypes") 1160 { 1161 rProp.Value >>= customFragmentTypes; 1162 } 1163 else if (propName == "OOXCustomFragmentTargets") 1164 { 1165 rProp.Value >>= customFragmentTargets; 1166 } 1167 else if (propName == "OOXContentTypes") 1168 { 1169 rProp.Value >>= aContentTypes; 1170 } 1171 } 1172 1173 // Expect customXmlDomPropslist.getLength() == customXmlDomlist.getLength(). 1174 for (sal_Int32 j = 0; j < customXmlDomlist.getLength(); j++) 1175 { 1176 uno::Reference<xml::dom::XDocument> customXmlDom = customXmlDomlist[j]; 1177 uno::Reference<xml::dom::XDocument> customXmlDomProps = customXmlDomPropslist[j]; 1178 const OUString fragmentPath = "customXml/item" + OUString::number(j+1) + ".xml"; 1179 if (customXmlDom.is()) 1180 { 1181 addRelation(oox::getRelationship(Relationship::CUSTOMXML), OUString("../" + fragmentPath)); 1182 1183 uno::Reference<xml::sax::XSAXSerializable> serializer(customXmlDom, uno::UNO_QUERY); 1184 uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext()); 1185 writer->setOutputStream(openFragmentStream(fragmentPath, "application/xml")); 1186 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), 1187 uno::Sequence<beans::StringPair>()); 1188 } 1189 1190 if (customXmlDomProps.is()) 1191 { 1192 uno::Reference<xml::sax::XSAXSerializable> serializer(customXmlDomProps, uno::UNO_QUERY); 1193 uno::Reference<xml::sax::XWriter> writer = xml::sax::Writer::create(comphelper::getProcessComponentContext()); 1194 writer->setOutputStream(openFragmentStream("customXml/itemProps"+OUString::number(j+1)+".xml", 1195 "application/vnd.openxmlformats-officedocument.customXmlProperties+xml")); 1196 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW), 1197 uno::Sequence<beans::StringPair>()); 1198 1199 // Adding itemprops's relationship entry to item.xml.rels file 1200 addRelation(openFragmentStream(fragmentPath, "application/xml"), 1201 oox::getRelationship(Relationship::CUSTOMXMLPROPS), 1202 OUString("itemProps"+OUString::number(j+1)+".xml")); 1203 } 1204 } 1205 1206 // Expect customFragments.getLength() == customFragmentTypes.getLength() == customFragmentTargets.getLength(). 1207 for (sal_Int32 j = 0; j < customFragments.getLength(); j++) 1208 { 1209 addRelation(customFragmentTypes[j], customFragmentTargets[j]); 1210 const OUString aFilename = customFragmentTargets[j]; 1211 Reference<XOutputStream> xOutStream = openOutputStream(aFilename); 1212 if (xOutStream.is()) 1213 { 1214 xOutStream->writeBytes(customFragments[j]); 1215 uno::Reference<XPropertySet> xProps(xOutStream, uno::UNO_QUERY); 1216 if (xProps.is()) 1217 { 1218 const OUString aType = comphelper::OFOPXMLHelper::GetContentTypeByName(aContentTypes, aFilename); 1219 const OUString aContentType = (aType.getLength() ? aType : OUString("application/octet-stream")); 1220 xProps->setPropertyValue("MediaType", uno::makeAny(aContentType)); 1221 } 1222 } 1223 } 1224 } 1225 1226 } // namespace oox::core 1227 1228 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1229
