1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
24 #include <filter/msfilter/util.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <o3tl/sprintf.hxx>
27 #include <osl/diagnose.h>
28 #include <rtl/ustring.hxx>
29 #include <rtl/ustrbuf.hxx>
30 #include <rtl/random.h>
31 #include <sax/fshelper.hxx>
32 #include <unotools/streamwrap.hxx>
33 #include <sot/storage.hxx>
34 #include <tools/urlobj.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/settings.hxx>
37 #include <officecfg/Office/Calc.hxx>
40 #include <xestream.hxx>
41 #include <xladdress.hxx>
42 #include <xlstring.hxx>
43 #include <xltools.hxx>
45 #include <xestring.hxx>
46 #include <xlstyle.hxx>
47 #include <rangelst.hxx>
48 #include <compiler.hxx>
49 #include <formulacell.hxx>
50 #include <tokenarray.hxx>
51 #include <tokenstringcontext.hxx>
52 #include <refreshtimerprotector.hxx>
53 #include <globstr.hrc>
54 #include <scresid.hxx>
56 #include <sfx2/app.hxx>
59 #include <tabvwsh.hxx>
60 #include <viewdata.hxx>
63 #include <oox/token/tokens.hxx>
64 #include <oox/token/relationship.hxx>
65 #include <oox/export/drawingml.hxx>
66 #include <oox/export/utils.hxx>
67 #include <oox/export/ColorExportUtils.hxx>
68 #include <formula/grammar.hxx>
69 #include <oox/ole/vbaexport.hxx>
70 #include <excelvbaproject.hxx>
72 #include <com/sun/star/task/XStatusIndicator.hpp>
74 #include <comphelper/servicehelper.hxx>
75 #include <comphelper/storagehelper.hxx>
77 #include <externalrefmgr.hxx>
79 #define DEBUG_XL_ENCRYPTION 0
81 using ::com::sun::star::uno::XInterface
;
84 using namespace com::sun::star
;
85 using namespace ::com::sun::star::beans
;
86 using namespace ::com::sun::star::io
;
87 using namespace ::com::sun::star::lang
;
88 using namespace ::com::sun::star::sheet
;
89 using namespace ::com::sun::star::uno
;
90 using namespace ::formula
;
91 using namespace ::oox
;
93 XclExpStream::XclExpStream( SvStream
& rOutStrm
, const XclExpRoot
& rRoot
, sal_uInt16 nMaxRecSize
) :
96 mbUseEncrypter( false ),
97 mnMaxRecSize( nMaxRecSize
),
107 if( mnMaxRecSize
== 0 )
108 mnMaxRecSize
= (mrRoot
.GetBiff() <= EXC_BIFF5
) ? EXC_MAXRECSIZE_BIFF5
: EXC_MAXRECSIZE_BIFF8
;
109 mnMaxContSize
= mnMaxRecSize
;
112 XclExpStream::~XclExpStream()
114 mrStrm
.FlushBuffer();
117 void XclExpStream::StartRecord( sal_uInt16 nRecId
, std::size_t nRecSize
)
119 OSL_ENSURE( !mbInRec
, "XclExpStream::StartRecord - another record still open" );
121 mnMaxContSize
= mnCurrMaxSize
= mnMaxRecSize
;
122 mnPredictSize
= nRecSize
;
124 InitRecord( nRecId
);
129 void XclExpStream::EndRecord()
131 OSL_ENSURE( mbInRec
, "XclExpStream::EndRecord - no record open" );
134 mrStrm
.Seek( STREAM_SEEK_TO_END
);
138 void XclExpStream::SetSliceSize( sal_uInt16 nSize
)
140 mnMaxSliceSize
= nSize
;
144 XclExpStream
& XclExpStream::operator<<( sal_Int8 nValue
)
147 if (mbUseEncrypter
&& HasValidEncrypter())
148 mxEncrypter
->Encrypt(mrStrm
, nValue
);
150 mrStrm
.WriteSChar( nValue
);
154 XclExpStream
& XclExpStream::operator<<( sal_uInt8 nValue
)
157 if (mbUseEncrypter
&& HasValidEncrypter())
158 mxEncrypter
->Encrypt(mrStrm
, nValue
);
160 mrStrm
.WriteUChar( nValue
);
164 XclExpStream
& XclExpStream::operator<<( sal_Int16 nValue
)
167 if (mbUseEncrypter
&& HasValidEncrypter())
168 mxEncrypter
->Encrypt(mrStrm
, nValue
);
170 mrStrm
.WriteInt16( nValue
);
174 XclExpStream
& XclExpStream::operator<<( sal_uInt16 nValue
)
177 if (mbUseEncrypter
&& HasValidEncrypter())
178 mxEncrypter
->Encrypt(mrStrm
, nValue
);
180 mrStrm
.WriteUInt16( nValue
);
184 XclExpStream
& XclExpStream::operator<<( sal_Int32 nValue
)
187 if (mbUseEncrypter
&& HasValidEncrypter())
188 mxEncrypter
->Encrypt(mrStrm
, nValue
);
190 mrStrm
.WriteInt32( nValue
);
194 XclExpStream
& XclExpStream::operator<<( sal_uInt32 nValue
)
197 if (mbUseEncrypter
&& HasValidEncrypter())
198 mxEncrypter
->Encrypt(mrStrm
, nValue
);
200 mrStrm
.WriteUInt32( nValue
);
204 XclExpStream
& XclExpStream::operator<<( float fValue
)
207 if (mbUseEncrypter
&& HasValidEncrypter())
208 mxEncrypter
->Encrypt(mrStrm
, fValue
);
210 mrStrm
.WriteFloat( fValue
);
214 XclExpStream
& XclExpStream::operator<<( double fValue
)
217 if (mbUseEncrypter
&& HasValidEncrypter())
218 mxEncrypter
->Encrypt(mrStrm
, fValue
);
220 mrStrm
.WriteDouble( fValue
);
224 std::size_t XclExpStream::Write( const void* pData
, std::size_t nBytes
)
226 std::size_t nRet
= 0;
227 if( pData
&& (nBytes
> 0) )
231 const sal_uInt8
* pBuffer
= static_cast< const sal_uInt8
* >( pData
);
232 std::size_t nBytesLeft
= nBytes
;
235 while( bValid
&& (nBytesLeft
> 0) )
237 std::size_t nWriteLen
= ::std::min
< std::size_t >( PrepareWrite(), nBytesLeft
);
238 std::size_t nWriteRet
= nWriteLen
;
239 if (mbUseEncrypter
&& HasValidEncrypter())
241 OSL_ENSURE(nWriteLen
> 0, "XclExpStream::Write: write length is 0!");
242 vector
<sal_uInt8
> aBytes(nWriteLen
);
243 memcpy(aBytes
.data(), pBuffer
, nWriteLen
);
244 mxEncrypter
->EncryptBytes(mrStrm
, aBytes
);
245 // TODO: How do I check if all the bytes have been successfully written ?
249 nWriteRet
= mrStrm
.WriteBytes(pBuffer
, nWriteLen
);
250 bValid
= (nWriteLen
== nWriteRet
);
251 OSL_ENSURE( bValid
, "XclExpStream::Write - stream write error" );
253 pBuffer
+= nWriteRet
;
255 nBytesLeft
-= nWriteRet
;
256 UpdateSizeVars( nWriteRet
);
260 nRet
= mrStrm
.WriteBytes(pData
, nBytes
);
265 void XclExpStream::WriteZeroBytes( std::size_t nBytes
)
269 std::size_t nBytesLeft
= nBytes
;
270 while( nBytesLeft
> 0 )
272 std::size_t nWriteLen
= ::std::min
< std::size_t >( PrepareWrite(), nBytesLeft
);
273 WriteRawZeroBytes( nWriteLen
);
274 nBytesLeft
-= nWriteLen
;
275 UpdateSizeVars( nWriteLen
);
279 WriteRawZeroBytes( nBytes
);
282 void XclExpStream::WriteZeroBytesToRecord( std::size_t nBytes
)
288 for (std::size_t i
= 0; i
< nBytes
; ++i
)
289 *this << sal_uInt8(0)/*nZero*/;
292 void XclExpStream::CopyFromStream(SvStream
& rInStrm
, sal_uInt64
const nBytes
)
294 sal_uInt64
const nRemaining(rInStrm
.remainingSize());
295 sal_uInt64 nBytesLeft
= ::std::min(nBytes
, nRemaining
);
296 if( nBytesLeft
<= 0 )
299 const std::size_t nMaxBuffer
= 4096;
300 std::unique_ptr
<sal_uInt8
[]> pBuffer(
301 new sal_uInt8
[ ::std::min
<std::size_t>(nBytesLeft
, nMaxBuffer
) ]);
304 while( bValid
&& (nBytesLeft
> 0) )
306 std::size_t nWriteLen
= ::std::min
<std::size_t>(nBytesLeft
, nMaxBuffer
);
307 rInStrm
.ReadBytes(pBuffer
.get(), nWriteLen
);
308 std::size_t nWriteRet
= Write( pBuffer
.get(), nWriteLen
);
309 bValid
= (nWriteLen
== nWriteRet
);
310 nBytesLeft
-= nWriteRet
;
314 void XclExpStream::WriteUnicodeBuffer( const ScfUInt16Vec
& rBuffer
, sal_uInt8 nFlags
)
317 nFlags
&= EXC_STRF_16BIT
; // repeat only 16bit flag
318 sal_uInt16 nCharLen
= nFlags
? 2 : 1;
320 for( const auto& rItem
: rBuffer
)
322 if( mbInRec
&& (mnCurrSize
+ nCharLen
> mnCurrMaxSize
) )
325 operator<<( nFlags
);
330 operator<<( static_cast< sal_uInt8
>( rItem
) );
334 // Xcl has an obscure sense of whether starting a new record or not,
335 // and crashes if it encounters the string header at the very end of a record.
336 // Thus we add 1 to give some room, seems like they do it that way but with another count (10?)
337 void XclExpStream::WriteByteString( const OString
& rString
)
340 std::size_t nLen
= ::std::min
< std::size_t >( rString
.getLength(), 0x00FF );
341 nLen
= ::std::min
< std::size_t >( nLen
, 0xFF );
343 sal_uInt16 nLeft
= PrepareWrite();
344 if( mbInRec
&& (nLeft
<= 1) )
347 operator<<( static_cast< sal_uInt8
>( nLen
) );
348 Write( rString
.getStr(), nLen
);
351 void XclExpStream::WriteCharBuffer( const ScfUInt8Vec
& rBuffer
)
354 Write( rBuffer
.data(), rBuffer
.size() );
357 void XclExpStream::SetEncrypter( XclExpEncrypterRef
const & xEncrypter
)
359 mxEncrypter
= xEncrypter
;
362 bool XclExpStream::HasValidEncrypter() const
364 return mxEncrypter
&& mxEncrypter
->IsValid();
367 void XclExpStream::EnableEncryption( bool bEnable
)
369 mbUseEncrypter
= bEnable
&& HasValidEncrypter();
372 void XclExpStream::DisableEncryption()
374 EnableEncryption(false);
377 void XclExpStream::SetSvStreamPos(sal_uInt64
const nPos
)
379 OSL_ENSURE( !mbInRec
, "XclExpStream::SetSvStreamPos - not allowed inside of a record" );
380 mbInRec
? 0 : mrStrm
.Seek( nPos
);
383 // private --------------------------------------------------------------------
385 void XclExpStream::InitRecord( sal_uInt16 nRecId
)
387 mrStrm
.Seek( STREAM_SEEK_TO_END
);
388 mrStrm
.WriteUInt16( nRecId
);
390 mnLastSizePos
= mrStrm
.Tell();
391 mnHeaderSize
= static_cast< sal_uInt16
>( ::std::min
< std::size_t >( mnPredictSize
, mnCurrMaxSize
) );
392 mrStrm
.WriteUInt16( mnHeaderSize
);
393 mnCurrSize
= mnSliceSize
= 0;
396 void XclExpStream::UpdateRecSize()
398 if( mnCurrSize
!= mnHeaderSize
)
400 mrStrm
.Seek( mnLastSizePos
);
401 mrStrm
.WriteUInt16( mnCurrSize
);
405 void XclExpStream::UpdateSizeVars( std::size_t nSize
)
407 OSL_ENSURE( mnCurrSize
+ nSize
<= mnCurrMaxSize
, "XclExpStream::UpdateSizeVars - record overwritten" );
408 mnCurrSize
= mnCurrSize
+ static_cast< sal_uInt16
>( nSize
);
410 if( mnMaxSliceSize
> 0 )
412 OSL_ENSURE( mnSliceSize
+ nSize
<= mnMaxSliceSize
, "XclExpStream::UpdateSizeVars - slice overwritten" );
413 mnSliceSize
= mnSliceSize
+ static_cast< sal_uInt16
>( nSize
);
414 if( mnSliceSize
>= mnMaxSliceSize
)
419 void XclExpStream::StartContinue()
422 mnCurrMaxSize
= mnMaxContSize
;
423 mnPredictSize
-= mnCurrSize
;
424 InitRecord( EXC_ID_CONT
);
427 void XclExpStream::PrepareWrite( sal_uInt16 nSize
)
431 if( (mnCurrSize
+ nSize
> mnCurrMaxSize
) ||
432 ((mnMaxSliceSize
> 0) && (mnSliceSize
== 0) && (mnCurrSize
+ mnMaxSliceSize
> mnCurrMaxSize
)) )
434 UpdateSizeVars( nSize
);
438 sal_uInt16
XclExpStream::PrepareWrite()
443 if( (mnCurrSize
>= mnCurrMaxSize
) ||
444 ((mnMaxSliceSize
> 0) && (mnSliceSize
== 0) && (mnCurrSize
+ mnMaxSliceSize
> mnCurrMaxSize
)) )
448 nRet
= (mnMaxSliceSize
> 0) ? (mnMaxSliceSize
- mnSliceSize
) : (mnCurrMaxSize
- mnCurrSize
);
453 void XclExpStream::WriteRawZeroBytes( std::size_t nBytes
)
455 const sal_uInt32 nData
= 0;
456 std::size_t nBytesLeft
= nBytes
;
457 while( nBytesLeft
>= sizeof( nData
) )
459 mrStrm
.WriteUInt32( nData
);
460 nBytesLeft
-= sizeof( nData
);
463 mrStrm
.WriteBytes(&nData
, nBytesLeft
);
466 XclExpBiff8Encrypter::XclExpBiff8Encrypter( const XclExpRoot
& rRoot
) :
467 mnOldPos(STREAM_SEEK_TO_END
),
470 Sequence
< NamedValue
> aEncryptionData
= rRoot
.GetEncryptionData();
471 if( !aEncryptionData
.hasElements() )
472 // Empty password. Get the default biff8 password.
473 aEncryptionData
= XclExpRoot::GenerateDefaultEncryptionData();
474 Init( aEncryptionData
);
477 XclExpBiff8Encrypter::~XclExpBiff8Encrypter()
481 void XclExpBiff8Encrypter::GetSaltDigest( sal_uInt8 pnSaltDigest
[16] ) const
483 if ( sizeof( mpnSaltDigest
) == 16 )
484 memcpy( pnSaltDigest
, mpnSaltDigest
, 16 );
487 void XclExpBiff8Encrypter::GetSalt( sal_uInt8 pnSalt
[16] ) const
489 if ( sizeof( mpnSalt
) == 16 )
490 memcpy( pnSalt
, mpnSalt
, 16 );
493 void XclExpBiff8Encrypter::GetDocId( sal_uInt8 pnDocId
[16] ) const
495 if ( sizeof( mpnDocId
) == 16 )
496 memcpy( pnDocId
, mpnDocId
, 16 );
499 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_uInt8 nData
)
501 vector
<sal_uInt8
> aByte
{ nData
};
502 EncryptBytes(rStrm
, aByte
);
505 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_uInt16 nData
)
507 ::std::vector
<sal_uInt8
> pnBytes
509 o3tl::narrowing
<sal_uInt8
>(nData
& 0xFF),
510 o3tl::narrowing
<sal_uInt8
>((nData
>> 8) & 0xFF)
512 EncryptBytes(rStrm
, pnBytes
);
515 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_uInt32 nData
)
517 ::std::vector
<sal_uInt8
> pnBytes
519 o3tl::narrowing
<sal_uInt8
>(nData
& 0xFF),
520 o3tl::narrowing
<sal_uInt8
>((nData
>> 8) & 0xFF),
521 o3tl::narrowing
<sal_uInt8
>((nData
>> 16) & 0xFF),
522 o3tl::narrowing
<sal_uInt8
>((nData
>> 24) & 0xFF)
524 EncryptBytes(rStrm
, pnBytes
);
527 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, float fValue
)
529 ::std::vector
<sal_uInt8
> pnBytes(4);
530 memcpy(pnBytes
.data(), &fValue
, 4);
531 EncryptBytes(rStrm
, pnBytes
);
534 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, double fValue
)
536 ::std::vector
<sal_uInt8
> pnBytes(8);
537 memcpy(pnBytes
.data(), &fValue
, 8);
538 EncryptBytes(rStrm
, pnBytes
);
541 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_Int8 nData
)
543 Encrypt(rStrm
, static_cast<sal_uInt8
>(nData
));
546 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_Int16 nData
)
548 Encrypt(rStrm
, static_cast<sal_uInt16
>(nData
));
551 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_Int32 nData
)
553 Encrypt(rStrm
, static_cast<sal_uInt32
>(nData
));
556 void XclExpBiff8Encrypter::Init( const Sequence
< NamedValue
>& rEncryptionData
)
560 if( !maCodec
.InitCodec( rEncryptionData
) )
563 maCodec
.GetDocId( mpnDocId
);
565 // generate the salt here
566 if (rtl_random_getBytes(nullptr, mpnSalt
, 16) != rtl_Random_E_None
)
568 throw uno::RuntimeException(u
"rtl_random_getBytes failed"_ustr
);
571 memset( mpnSaltDigest
, 0, sizeof( mpnSaltDigest
) );
573 // generate salt hash.
574 ::msfilter::MSCodec_Std97 aCodec
;
575 aCodec
.InitCodec( rEncryptionData
);
576 aCodec
.CreateSaltDigest( mpnSalt
, mpnSaltDigest
);
578 // verify to make sure it's in good shape.
579 mbValid
= maCodec
.VerifyKey( mpnSalt
, mpnSaltDigest
);
582 sal_uInt32
XclExpBiff8Encrypter::GetBlockPos( std::size_t nStrmPos
)
584 return static_cast< sal_uInt32
>( nStrmPos
/ EXC_ENCR_BLOCKSIZE
);
587 sal_uInt16
XclExpBiff8Encrypter::GetOffsetInBlock( std::size_t nStrmPos
)
589 return static_cast< sal_uInt16
>( nStrmPos
% EXC_ENCR_BLOCKSIZE
);
592 void XclExpBiff8Encrypter::EncryptBytes( SvStream
& rStrm
, vector
<sal_uInt8
>& aBytes
)
594 sal_uInt64 nStrmPos
= rStrm
.Tell();
595 sal_uInt16 nBlockOffset
= GetOffsetInBlock(nStrmPos
);
596 sal_uInt32 nBlockPos
= GetBlockPos(nStrmPos
);
598 SAL_INFO("sc.filter", "XclExpBiff8Encrypter::EncryptBytes: stream pos = "
599 << nStrmPos
<< " offset in block = " << nBlockOffset
600 << " block pos = " << nBlockPos
);
602 sal_uInt16 nSize
= static_cast< sal_uInt16
>( aBytes
.size() );
606 #if DEBUG_XL_ENCRYPTION
607 fprintf(stdout
, "RAW: ");
608 for (sal_uInt16 i
= 0; i
< nSize
; ++i
)
609 fprintf(stdout
, "%2.2X ", aBytes
[i
]);
610 fprintf(stdout
, "\n");
613 if (mnOldPos
!= nStrmPos
)
615 sal_uInt16 nOldOffset
= GetOffsetInBlock(mnOldPos
);
616 sal_uInt32 nOldBlockPos
= GetBlockPos(mnOldPos
);
618 if ( (nBlockPos
!= nOldBlockPos
) || (nBlockOffset
< nOldOffset
) )
620 maCodec
.InitCipher(nBlockPos
);
624 if (nBlockOffset
> nOldOffset
)
625 maCodec
.Skip(nBlockOffset
- nOldOffset
);
628 sal_uInt16 nBytesLeft
= nSize
;
630 while (nBytesLeft
> 0)
632 sal_uInt16 nBlockLeft
= EXC_ENCR_BLOCKSIZE
- nBlockOffset
;
633 sal_uInt16 nEncBytes
= ::std::min(nBlockLeft
, nBytesLeft
);
635 bool bRet
= maCodec
.Encode(&aBytes
[nPos
], nEncBytes
, &aBytes
[nPos
], nEncBytes
);
636 OSL_ENSURE(bRet
, "XclExpBiff8Encrypter::EncryptBytes: encryption failed!!");
638 std::size_t nRet
= rStrm
.WriteBytes(&aBytes
[nPos
], nEncBytes
);
639 OSL_ENSURE(nRet
== nEncBytes
, "XclExpBiff8Encrypter::EncryptBytes: fail to write to stream!!");
641 nStrmPos
= rStrm
.Tell();
642 nBlockOffset
= GetOffsetInBlock(nStrmPos
);
643 nBlockPos
= GetBlockPos(nStrmPos
);
644 if (nBlockOffset
== 0)
645 maCodec
.InitCipher(nBlockPos
);
647 nBytesLeft
-= nEncBytes
;
653 static const char* lcl_GetErrorString( FormulaError nScErrCode
)
655 sal_uInt8 nXclErrCode
= XclTools::GetXclErrorCode( nScErrCode
);
656 switch( nXclErrCode
)
658 case EXC_ERR_NULL
: return "#NULL!";
659 case EXC_ERR_DIV0
: return "#DIV/0!";
660 case EXC_ERR_VALUE
: return "#VALUE!";
661 case EXC_ERR_REF
: return "#REF!";
662 case EXC_ERR_NAME
: return "#NAME?";
663 case EXC_ERR_NUM
: return "#NUM!";
665 default: return "#N/A";
669 void XclXmlUtils::GetFormulaTypeAndValue( ScFormulaCell
& rCell
, const char*& rsType
, OUString
& rsValue
)
671 sc::FormulaResultValue aResValue
= rCell
.GetResult();
673 switch (aResValue
.meType
)
675 case sc::FormulaResultValue::Error
:
677 rsValue
= ToOUString(lcl_GetErrorString(aResValue
.mnError
));
679 case sc::FormulaResultValue::Value
:
680 rsType
= rCell
.GetFormatType() == SvNumFormatType::LOGICAL
681 && (aResValue
.mfValue
== 0.0 || aResValue
.mfValue
== 1.0)
684 rsValue
= OUString::number(aResValue
.mfValue
);
686 case sc::FormulaResultValue::String
:
688 rsValue
= rCell
.GetString().getString();
690 case sc::FormulaResultValue::Invalid
:
692 // TODO : double-check this to see if this is correct.
693 rsType
= "inlineStr";
694 rsValue
= rCell
.GetString().getString();
698 OUString
XclXmlUtils::GetStreamName( const char* sStreamDir
, const char* sStream
, sal_Int32 nId
)
702 sBuf
.appendAscii( sStreamDir
);
703 sBuf
.appendAscii( sStream
);
706 if( strstr(sStream
, "vml") )
707 sBuf
.append( ".vml" );
709 sBuf
.append( ".xml" );
710 return sBuf
.makeStringAndClear();
713 OString
XclXmlUtils::ToOString( const Color
& rColor
)
716 o3tl::sprintf( buf
, "%.2X%.2X%.2X%.2X", rColor
.GetAlpha(), rColor
.GetRed(), rColor
.GetGreen(), rColor
.GetBlue() );
721 OStringBuffer
& XclXmlUtils::ToOString( OStringBuffer
& s
, const ScAddress
& rAddress
)
723 rAddress
.Format(s
, ScRefFlags::VALID
, nullptr, ScAddress::Details( FormulaGrammar::CONV_XL_A1
));
727 OString
XclXmlUtils::ToOString( const ScfUInt16Vec
& rBuffer
)
732 const sal_uInt16
* pBuffer
= rBuffer
.data();
734 reinterpret_cast<sal_Unicode
const *>(pBuffer
), rBuffer
.size(),
735 RTL_TEXTENCODING_UTF8
);
738 OString
XclXmlUtils::ToOString( const ScDocument
& rDoc
, const ScRange
& rRange
, bool bFullAddressNotation
)
740 OUString
sRange(rRange
.Format( rDoc
, ScRefFlags::VALID
,
741 ScAddress::Details( FormulaGrammar::CONV_XL_A1
),
742 bFullAddressNotation
) );
743 return sRange
.toUtf8();
746 OString
XclXmlUtils::ToOString( const ScDocument
& rDoc
, const ScRangeList
& rRangeList
)
749 rRangeList
.Format(s
, ScRefFlags::VALID
, rDoc
, FormulaGrammar::CONV_XL_OOX
, ' ');
753 static ScAddress
lcl_ToAddress( const XclAddress
& rAddress
)
755 return ScAddress( rAddress
.mnCol
, rAddress
.mnRow
, 0 );
758 OStringBuffer
& XclXmlUtils::ToOString( OStringBuffer
& s
, const XclAddress
& rAddress
)
760 return ToOString( s
, lcl_ToAddress( rAddress
));
763 OString
XclXmlUtils::ToOString( const XclExpString
& s
)
765 OSL_ENSURE( !s
.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
766 return ToOString( s
.GetUnicodeBuffer() );
769 static ScRange
lcl_ToRange( const XclRange
& rRange
)
773 aRange
.aStart
= lcl_ToAddress( rRange
.maFirst
);
774 aRange
.aEnd
= lcl_ToAddress( rRange
.maLast
);
779 OString
XclXmlUtils::ToOString( const ScDocument
& rDoc
, const XclRangeList
& rRanges
)
782 for( const auto& rRange
: rRanges
)
784 aRanges
.push_back( lcl_ToRange( rRange
) );
786 return ToOString( rDoc
, aRanges
);
789 OUString
XclXmlUtils::ToOUString( const char* s
)
791 return OUString( s
, static_cast<sal_Int32
>(strlen( s
)), RTL_TEXTENCODING_ASCII_US
);
794 OUString
XclXmlUtils::ToOUString( const ScfUInt16Vec
& rBuf
, sal_Int32 nStart
, sal_Int32 nLength
)
796 if( nLength
== -1 || ( nLength
> (static_cast<sal_Int32
>(rBuf
.size()) - nStart
) ) )
797 nLength
= (rBuf
.size() - nStart
);
801 reinterpret_cast<sal_Unicode
const *>(&rBuf
[nStart
]), nLength
)
805 OUString
XclXmlUtils::ToOUString(
806 sc::CompileFormulaContext
& rCtx
, const ScAddress
& rAddress
, const ScTokenArray
* pTokenArray
,
807 FormulaError nErrCode
)
809 ScCompiler
aCompiler( rCtx
, rAddress
, const_cast<ScTokenArray
&>(*pTokenArray
));
811 /* TODO: isn't this the same as passed in rCtx and thus superfluous? */
812 aCompiler
.SetGrammar(FormulaGrammar::GRAM_OOXML
);
814 sal_Int32 nLen
= pTokenArray
->GetLen();
815 OUStringBuffer
aBuffer( nLen
? (nLen
* 5) : 8 );
817 aCompiler
.CreateStringFromTokenArray( aBuffer
);
820 if (nErrCode
!= FormulaError::NONE
)
821 aCompiler
.AppendErrorConstant( aBuffer
, nErrCode
);
824 // No code SHOULD be an "error cell", assert caller thought of that
826 assert(!"No code and no error.");
830 return aBuffer
.makeStringAndClear();
833 OUString
XclXmlUtils::ToOUString( const XclExpString
& s
)
835 OSL_ENSURE( !s
.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
836 return ToOUString( s
.GetUnicodeBuffer() );
839 static const char* lcl_GetUnderlineStyle( FontLineStyle eUnderline
, bool& bHaveUnderline
)
841 bHaveUnderline
= true;
844 // OOXTODO: doubleAccounting, singleAccounting
845 // OOXTODO: what should be done with the other FontLineStyle values?
846 case LINESTYLE_SINGLE
: return "single";
847 case LINESTYLE_DOUBLE
: return "double";
849 default: bHaveUnderline
= false; return "none";
853 static const char* lcl_ToVerticalAlignmentRun( SvxEscapement eEscapement
, bool& bHaveAlignment
)
855 bHaveAlignment
= true;
856 switch( eEscapement
)
858 case SvxEscapement::Superscript
: return "superscript";
859 case SvxEscapement::Subscript
: return "subscript";
860 case SvxEscapement::Off
:
861 default: bHaveAlignment
= false; return "baseline";
865 sax_fastparser::FSHelperPtr
XclXmlUtils::WriteFontData( sax_fastparser::FSHelperPtr pStream
, const XclFontData
& rFontData
, sal_Int32 nFontId
)
867 bool bHaveUnderline
, bHaveVertAlign
;
868 const char* pUnderline
= lcl_GetUnderlineStyle( rFontData
.GetScUnderline(), bHaveUnderline
);
869 const char* pVertAlign
= lcl_ToVerticalAlignmentRun( rFontData
.GetScEscapement(), bHaveVertAlign
);
871 if (rFontData
.mnWeight
> 400)
872 pStream
->singleElement(XML_b
, XML_val
, ToPsz( true ));
873 if (rFontData
.mbItalic
)
874 pStream
->singleElement(XML_i
, XML_val
, ToPsz( true ));
875 if (rFontData
.mbStrikeout
)
876 pStream
->singleElement(XML_strike
, XML_val
, ToPsz( true ));
877 // OOXTODO: lcl_WriteValue( rStream, XML_condense, ); // mac compatibility setting
878 // OOXTODO: lcl_WriteValue( rStream, XML_extend, ); // compatibility setting
879 if (rFontData
.mbOutline
)
880 pStream
->singleElement(XML_outline
, XML_val
, ToPsz( true ));
881 if (rFontData
.mbShadow
)
882 pStream
->singleElement(XML_shadow
, XML_val
, ToPsz( true ));
884 pStream
->singleElement(XML_u
, XML_val
, pUnderline
);
886 pStream
->singleElement(XML_vertAlign
, XML_val
, pVertAlign
);
887 pStream
->singleElement(XML_sz
, XML_val
, OString::number( rFontData
.mnHeight
/ 20.0 )); // Twips->Pt
889 auto& rComplexColor
= rFontData
.maComplexColor
;
890 if (rComplexColor
.isValidThemeType())
892 sal_Int32 nTheme
= oox::convertThemeColorTypeToExcelThemeNumber(rComplexColor
.getThemeColorType());
893 double fTintShade
= oox::convertColorTransformsToTintOrShade(rComplexColor
);
894 pStream
->singleElement(XML_color
,
895 XML_theme
, OString::number(nTheme
),
896 XML_tint
, sax_fastparser::UseIf(OString::number(fTintShade
), fTintShade
!= 0.0));
898 else if (rComplexColor
.getFinalColor() != Color( ColorAlpha
, 0, 0xFF, 0xFF, 0xFF))
900 pStream
->singleElement(XML_color
,
901 XML_rgb
, XclXmlUtils::ToOString(rComplexColor
.getFinalColor()));
903 pStream
->singleElement(nFontId
, XML_val
, rFontData
.maName
);
904 pStream
->singleElement(XML_family
, XML_val
, OString::number( rFontData
.mnFamily
));
905 if (rFontData
.mnCharSet
!= 0)
906 pStream
->singleElement(XML_charset
, XML_val
, OString::number(rFontData
.mnCharSet
));
911 XclExpXmlStream::XclExpXmlStream( const uno::Reference
< XComponentContext
>& rCC
, bool bExportVBA
, bool bExportTemplate
)
912 : XmlFilterBase( rCC
),
914 mbExportVBA(bExportVBA
),
915 mbExportTemplate(bExportTemplate
)
919 XclExpXmlStream::~XclExpXmlStream()
921 assert(maStreams
.empty() && "Forgotten PopStream()?");
924 sax_fastparser::FSHelperPtr
& XclExpXmlStream::GetCurrentStream()
926 OSL_ENSURE( !maStreams
.empty(), "XclExpXmlStream::GetCurrentStream - no current stream" );
927 return maStreams
.top();
930 void XclExpXmlStream::PushStream( sax_fastparser::FSHelperPtr
const & aStream
)
932 maStreams
.push( aStream
);
935 void XclExpXmlStream::PopStream()
937 OSL_ENSURE( !maStreams
.empty(), "XclExpXmlStream::PopStream - stack is empty!" );
941 sax_fastparser::FSHelperPtr
XclExpXmlStream::GetStreamForPath( const OUString
& sPath
)
943 if( maOpenedStreamMap
.find( sPath
) == maOpenedStreamMap
.end() )
944 return sax_fastparser::FSHelperPtr();
945 return maOpenedStreamMap
[ sPath
].second
;
948 void XclExpXmlStream::WriteAttribute(sal_Int32 nAttr
, std::u16string_view sVal
)
950 GetCurrentStream()->write(" ")->writeId(nAttr
)->write("=\"")->writeEscaped(sVal
)->write("\"");
953 sax_fastparser::FSHelperPtr
XclExpXmlStream::CreateOutputStream (
954 const OUString
& sFullStream
,
955 std::u16string_view sRelativeStream
,
956 const uno::Reference
< XOutputStream
>& xParentRelation
,
957 const char* sContentType
,
958 std::u16string_view sRelationshipType
,
959 OUString
* pRelationshipId
)
961 OUString sRelationshipId
;
962 if (xParentRelation
.is())
963 sRelationshipId
= addRelation( xParentRelation
, OUString(sRelationshipType
), sRelativeStream
);
965 sRelationshipId
= addRelation( OUString(sRelationshipType
), sRelativeStream
);
967 if( pRelationshipId
)
968 *pRelationshipId
= sRelationshipId
;
970 sax_fastparser::FSHelperPtr p
= openFragmentStreamWithSerializer( sFullStream
, OUString::createFromAscii( sContentType
) );
972 maOpenedStreamMap
[ sFullStream
] = std::make_pair( sRelationshipId
, p
);
977 bool XclExpXmlStream::importDocument() noexcept
982 oox::vml::Drawing
* XclExpXmlStream::getVmlDrawing()
987 const oox::drawingml::Theme
* XclExpXmlStream::getCurrentTheme() const
992 oox::drawingml::table::TableStyleListPtr
XclExpXmlStream::getTableStyles()
994 return oox::drawingml::table::TableStyleListPtr();
997 oox::drawingml::chart::ChartConverter
* XclExpXmlStream::getChartConverter()
1003 ScDocShell
* XclExpXmlStream::getDocShell()
1005 uno::Reference
< XInterface
> xModel( getModel(), UNO_QUERY
);
1007 ScModelObj
*pObj
= comphelper::getFromUnoTunnel
< ScModelObj
>( xModel
);
1010 return static_cast < ScDocShell
* >( pObj
->GetEmbeddedObject() );
1015 bool XclExpXmlStream::exportDocument()
1017 ScDocShell
* pShell
= getDocShell();
1018 ScDocument
& rDoc
= pShell
->GetDocument();
1019 ScRefreshTimerProtector
aProt(rDoc
.GetRefreshTimerControlAddress());
1021 const bool bValidateTabNames
= officecfg::Office::Calc::Filter::Export::MS_Excel::TruncateLongSheetNames::get();
1022 std::vector
<OUString
> aOriginalTabNames
;
1023 if (bValidateTabNames
)
1025 validateTabNames(aOriginalTabNames
);
1028 uno::Reference
<task::XStatusIndicator
> xStatusIndicator
= getStatusIndicator();
1030 if (xStatusIndicator
.is())
1031 xStatusIndicator
->start(ScResId(STR_SAVE_DOC
), 100);
1033 // NOTE: Don't use SotStorage or SvStream any more, and never call
1034 // SfxMedium::GetOutStream() anywhere in the xlsx export filter code!
1035 // Instead, write via XOutputStream instance.
1036 rtl::Reference
<SotStorage
> rStorage
;
1037 drawingml::DrawingML::ResetMlCounters();
1039 auto& rGraphicExportCache
= drawingml::GraphicExportCache::get();
1041 rGraphicExportCache
.push();
1043 XclExpRootData
aData(
1044 EXC_BIFF8
, *pShell
->GetMedium (), rStorage
, rDoc
,
1045 msfilter::util::getBestTextEncodingFromLocale(
1046 Application::GetSettings().GetLanguageTag().getLocale()));
1047 aData
.meOutput
= EXC_OUTPUT_XML_2007
;
1048 aData
.maXclMaxPos
.Set( EXC_MAXCOL_XML_2007
, EXC_MAXROW_XML_2007
, EXC_MAXTAB_XML_2007
);
1049 aData
.maMaxPos
.SetCol( ::std::min( aData
.maScMaxPos
.Col(), aData
.maXclMaxPos
.Col() ) );
1050 aData
.maMaxPos
.SetRow( ::std::min( aData
.maScMaxPos
.Row(), aData
.maXclMaxPos
.Row() ) );
1051 aData
.maMaxPos
.SetTab( ::std::min( aData
.maScMaxPos
.Tab(), aData
.maXclMaxPos
.Tab() ) );
1052 aData
.mpCompileFormulaCxt
= std::make_shared
<sc::CompileFormulaContext
>(rDoc
);
1053 // set target path to get correct relative links to target document, not source
1054 INetURLObject
aPath(getFileUrl());
1055 aData
.maBasePath
= OUString("file:///" + aPath
.GetPath() + "\\").replace('\\', '/')
1057 .replaceFirst("file:////", "file:///");
1059 XclExpRoot
aRoot( aData
);
1062 aRoot
.GetOldRoot().pER
= &aRoot
;
1063 aRoot
.GetOldRoot().eDateiTyp
= Biff8
;
1064 // Get the viewsettings before processing
1065 if (ScViewData
* pViewData
= ScDocShell::GetViewData())
1066 pViewData
->WriteExtOptions( mpRoot
->GetExtDocOptions() );
1069 // Try to get ScViewData through the current ScDocShell
1070 ScTabViewShell
* pTabViewShell
= pShell
->GetBestViewShell( false );
1071 if ( pTabViewShell
)
1073 pViewData
= &pTabViewShell
->GetViewData();
1074 pViewData
->WriteExtOptions( mpRoot
->GetExtDocOptions() );
1078 static constexpr OUString workbook
= u
"xl/workbook.xml"_ustr
;
1079 const char* pWorkbookContentType
= nullptr;
1082 if (mbExportTemplate
)
1084 pWorkbookContentType
= "application/vnd.ms-excel.template.macroEnabled.main+xml";
1088 pWorkbookContentType
= "application/vnd.ms-excel.sheet.macroEnabled.main+xml";
1093 if (mbExportTemplate
)
1095 pWorkbookContentType
= "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml";
1099 pWorkbookContentType
= "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
1103 PushStream( CreateOutputStream( workbook
, workbook
,
1104 uno::Reference
<XOutputStream
>(),
1105 pWorkbookContentType
,
1106 oox::getRelationship(Relationship::OFFICEDOCUMENT
) ) );
1110 VbaExport
aExport(getModel());
1111 if (aExport
.containsVBAProject())
1113 SvMemoryStream
aVbaStream(4096, 4096);
1114 rtl::Reference
<SotStorage
> pVBAStorage(new SotStorage(aVbaStream
));
1115 aExport
.exportVBA( pVBAStorage
.get() );
1117 css::uno::Reference
<css::io::XInputStream
> xVBAStream(
1118 new utl::OInputStreamWrapper(aVbaStream
));
1119 css::uno::Reference
<css::io::XOutputStream
> xVBAOutput
=
1120 openFragmentStream(u
"xl/vbaProject.bin"_ustr
, u
"application/vnd.ms-office.vbaProject"_ustr
);
1121 comphelper::OStorageHelper::CopyInputToOutput(xVBAStream
, xVBAOutput
);
1123 addRelation(GetCurrentStream()->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT
), u
"vbaProject.bin");
1127 // destruct at the end of the block
1129 ExcDocument
aDocRoot( aRoot
);
1130 if (xStatusIndicator
.is())
1131 xStatusIndicator
->setValue(10);
1133 if (xStatusIndicator
.is())
1134 xStatusIndicator
->setValue(40);
1135 aDocRoot
.WriteXml( *this );
1136 rDoc
.GetExternalRefManager()->disableSkipUnusedFileIds();
1140 // Free all FSHelperPtr, to flush data before committing storage
1141 for (auto& entry
: maOpenedStreamMap
)
1143 if (!entry
.second
.second
)
1145 entry
.second
.second
->endDocument();
1147 maOpenedStreamMap
.clear();
1151 if (bValidateTabNames
)
1153 restoreTabNames(aOriginalTabNames
);
1156 if (xStatusIndicator
.is())
1157 xStatusIndicator
->end();
1160 rGraphicExportCache
.pop();
1165 ::oox::ole::VbaProject
* XclExpXmlStream::implCreateVbaProject() const
1167 return new ::oox::xls::ExcelVbaProject( getComponentContext(), uno::Reference
< XSpreadsheetDocument
>( getModel(), UNO_QUERY
) );
1170 OUString
XclExpXmlStream::getImplementationName()
1172 return u
"TODO"_ustr
;
1175 void XclExpXmlStream::validateTabNames(std::vector
<OUString
>& aOriginalTabNames
)
1177 const int MAX_TAB_NAME_LENGTH
= 31;
1179 ScDocShell
* pShell
= getDocShell();
1180 ScDocument
& rDoc
= pShell
->GetDocument();
1182 // get original names
1183 aOriginalTabNames
.resize(rDoc
.GetTableCount());
1184 for (SCTAB nTab
=0; nTab
< rDoc
.GetTableCount(); nTab
++)
1186 rDoc
.GetName(nTab
, aOriginalTabNames
[nTab
]);
1190 std::vector
<OUString
> aNewTabNames
;
1191 aNewTabNames
.reserve(rDoc
.GetTableCount());
1194 for (SCTAB nTab
=0; nTab
< rDoc
.GetTableCount(); nTab
++)
1196 const OUString
& rOriginalName
= aOriginalTabNames
[nTab
];
1197 if (rOriginalName
.getLength() > MAX_TAB_NAME_LENGTH
)
1201 // let's try just truncate "<first 31 chars>"
1202 if (aNewName
.isEmpty())
1204 aNewName
= rOriginalName
.copy(0, MAX_TAB_NAME_LENGTH
);
1205 if (aNewTabNames
.end() != std::find(aNewTabNames
.begin(), aNewTabNames
.end(), aNewName
) ||
1206 aOriginalTabNames
.end() != std::find(aOriginalTabNames
.begin(), aOriginalTabNames
.end(), aNewName
))
1208 // was found => let's use another tab name
1213 // let's try "<first N chars>-XXX" template
1214 for (int digits
=1; digits
<10 && aNewName
.isEmpty(); digits
++)
1216 const int rangeStart
= pow(10, digits
- 1);
1217 const int rangeEnd
= pow(10, digits
);
1219 for (int i
=rangeStart
; i
<rangeEnd
&& aNewName
.isEmpty(); i
++)
1221 aNewName
= OUString::Concat(rOriginalName
.subView(0, MAX_TAB_NAME_LENGTH
- 1 - digits
)) + "-" + OUString::number(i
);
1222 if (aNewTabNames
.end() != std::find(aNewTabNames
.begin(), aNewTabNames
.end(), aNewName
) ||
1223 aOriginalTabNames
.end() != std::find(aOriginalTabNames
.begin(), aOriginalTabNames
.end(), aNewName
))
1225 // was found => let's use another tab name
1231 if (!aNewName
.isEmpty())
1233 // new name was created => rename
1234 renameTab(nTab
, aNewName
);
1235 aNewTabNames
.push_back(aNewName
);
1239 // default: do not rename
1240 aNewTabNames
.push_back(rOriginalName
);
1245 // default: do not rename
1246 aNewTabNames
.push_back(rOriginalName
);
1251 void XclExpXmlStream::restoreTabNames(const std::vector
<OUString
>& aOriginalTabNames
)
1253 ScDocShell
* pShell
= getDocShell();
1254 ScDocument
& rDoc
= pShell
->GetDocument();
1256 for (SCTAB nTab
=0; nTab
< rDoc
.GetTableCount(); nTab
++)
1258 const OUString
& rOriginalName
= aOriginalTabNames
[nTab
];
1260 OUString rModifiedName
;
1261 rDoc
.GetName(nTab
, rModifiedName
);
1263 if (rOriginalName
!= rModifiedName
)
1265 renameTab(nTab
, rOriginalName
);
1270 void XclExpXmlStream::renameTab(SCTAB aTab
, const OUString
& aNewName
)
1272 ScDocShell
* pShell
= getDocShell();
1273 ScDocument
& rDoc
= pShell
->GetDocument();
1275 bool bAutoCalcShellDisabled
= rDoc
.IsAutoCalcShellDisabled();
1276 bool bIdleEnabled
= rDoc
.IsIdleEnabled();
1278 rDoc
.SetAutoCalcShellDisabled( true );
1279 rDoc
.EnableIdle(false);
1281 if (rDoc
.RenameTab(aTab
, aNewName
))
1283 SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScTablesChanged
));
1286 rDoc
.SetAutoCalcShellDisabled( bAutoCalcShellDisabled
);
1287 rDoc
.EnableIdle(bIdleEnabled
);
1290 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */