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 <rtl/ustring.hxx>
27 #include <rtl/ustrbuf.hxx>
28 #include <rtl/random.h>
29 #include <sax/fshelper.hxx>
30 #include <unotools/streamwrap.hxx>
31 #include <sot/storage.hxx>
32 #include <tools/urlobj.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/settings.hxx>
35 #include <officecfg/Office/Calc.hxx>
38 #include <xestream.hxx>
39 #include <xladdress.hxx>
40 #include <xlstring.hxx>
41 #include <xltools.hxx>
43 #include <xcl97rec.hxx>
44 #include <rangelst.hxx>
45 #include <compiler.hxx>
46 #include <formulacell.hxx>
47 #include <tokenarray.hxx>
48 #include <tokenstringcontext.hxx>
49 #include <refreshtimerprotector.hxx>
50 #include <globstr.hrc>
51 #include <scresid.hxx>
53 #include <sfx2/app.hxx>
56 #include <viewdata.hxx>
59 #include <oox/token/tokens.hxx>
60 #include <oox/token/relationship.hxx>
61 #include <oox/export/utils.hxx>
62 #include <formula/grammar.hxx>
63 #include <oox/ole/vbaexport.hxx>
64 #include <excelvbaproject.hxx>
66 #include <com/sun/star/task/XStatusIndicator.hpp>
68 #include <comphelper/storagehelper.hxx>
70 #include <externalrefmgr.hxx>
72 #define DEBUG_XL_ENCRYPTION 0
74 using ::com::sun::star::uno::XInterface
;
77 using namespace com::sun::star
;
78 using namespace ::com::sun::star::beans
;
79 using namespace ::com::sun::star::io
;
80 using namespace ::com::sun::star::lang
;
81 using namespace ::com::sun::star::sheet
;
82 using namespace ::com::sun::star::uno
;
83 using namespace ::formula
;
84 using namespace ::oox
;
86 XclExpStream::XclExpStream( SvStream
& rOutStrm
, const XclExpRoot
& rRoot
, sal_uInt16 nMaxRecSize
) :
89 mbUseEncrypter( false ),
90 mnMaxRecSize( nMaxRecSize
),
100 if( mnMaxRecSize
== 0 )
101 mnMaxRecSize
= (mrRoot
.GetBiff() <= EXC_BIFF5
) ? EXC_MAXRECSIZE_BIFF5
: EXC_MAXRECSIZE_BIFF8
;
102 mnMaxContSize
= mnMaxRecSize
;
105 XclExpStream::~XclExpStream()
110 void XclExpStream::StartRecord( sal_uInt16 nRecId
, std::size_t nRecSize
)
112 OSL_ENSURE( !mbInRec
, "XclExpStream::StartRecord - another record still open" );
114 mnMaxContSize
= mnCurrMaxSize
= mnMaxRecSize
;
115 mnPredictSize
= nRecSize
;
117 InitRecord( nRecId
);
122 void XclExpStream::EndRecord()
124 OSL_ENSURE( mbInRec
, "XclExpStream::EndRecord - no record open" );
127 mrStrm
.Seek( STREAM_SEEK_TO_END
);
131 void XclExpStream::SetSliceSize( sal_uInt16 nSize
)
133 mnMaxSliceSize
= nSize
;
137 XclExpStream
& XclExpStream::operator<<( sal_Int8 nValue
)
140 if (mbUseEncrypter
&& HasValidEncrypter())
141 mxEncrypter
->Encrypt(mrStrm
, nValue
);
143 mrStrm
.WriteSChar( nValue
);
147 XclExpStream
& XclExpStream::operator<<( sal_uInt8 nValue
)
150 if (mbUseEncrypter
&& HasValidEncrypter())
151 mxEncrypter
->Encrypt(mrStrm
, nValue
);
153 mrStrm
.WriteUChar( nValue
);
157 XclExpStream
& XclExpStream::operator<<( sal_Int16 nValue
)
160 if (mbUseEncrypter
&& HasValidEncrypter())
161 mxEncrypter
->Encrypt(mrStrm
, nValue
);
163 mrStrm
.WriteInt16( nValue
);
167 XclExpStream
& XclExpStream::operator<<( sal_uInt16 nValue
)
170 if (mbUseEncrypter
&& HasValidEncrypter())
171 mxEncrypter
->Encrypt(mrStrm
, nValue
);
173 mrStrm
.WriteUInt16( nValue
);
177 XclExpStream
& XclExpStream::operator<<( sal_Int32 nValue
)
180 if (mbUseEncrypter
&& HasValidEncrypter())
181 mxEncrypter
->Encrypt(mrStrm
, nValue
);
183 mrStrm
.WriteInt32( nValue
);
187 XclExpStream
& XclExpStream::operator<<( sal_uInt32 nValue
)
190 if (mbUseEncrypter
&& HasValidEncrypter())
191 mxEncrypter
->Encrypt(mrStrm
, nValue
);
193 mrStrm
.WriteUInt32( nValue
);
197 XclExpStream
& XclExpStream::operator<<( float fValue
)
200 if (mbUseEncrypter
&& HasValidEncrypter())
201 mxEncrypter
->Encrypt(mrStrm
, fValue
);
203 mrStrm
.WriteFloat( fValue
);
207 XclExpStream
& XclExpStream::operator<<( double fValue
)
210 if (mbUseEncrypter
&& HasValidEncrypter())
211 mxEncrypter
->Encrypt(mrStrm
, fValue
);
213 mrStrm
.WriteDouble( fValue
);
217 std::size_t XclExpStream::Write( const void* pData
, std::size_t nBytes
)
219 std::size_t nRet
= 0;
220 if( pData
&& (nBytes
> 0) )
224 const sal_uInt8
* pBuffer
= static_cast< const sal_uInt8
* >( pData
);
225 std::size_t nBytesLeft
= nBytes
;
228 while( bValid
&& (nBytesLeft
> 0) )
230 std::size_t nWriteLen
= ::std::min
< std::size_t >( PrepareWrite(), nBytesLeft
);
231 std::size_t nWriteRet
= nWriteLen
;
232 if (mbUseEncrypter
&& HasValidEncrypter())
234 OSL_ENSURE(nWriteLen
> 0, "XclExpStream::Write: write length is 0!");
235 vector
<sal_uInt8
> aBytes(nWriteLen
);
236 memcpy(aBytes
.data(), pBuffer
, nWriteLen
);
237 mxEncrypter
->EncryptBytes(mrStrm
, aBytes
);
238 // TODO: How do I check if all the bytes have been successfully written ?
242 nWriteRet
= mrStrm
.WriteBytes(pBuffer
, nWriteLen
);
243 bValid
= (nWriteLen
== nWriteRet
);
244 OSL_ENSURE( bValid
, "XclExpStream::Write - stream write error" );
246 pBuffer
+= nWriteRet
;
248 nBytesLeft
-= nWriteRet
;
249 UpdateSizeVars( nWriteRet
);
253 nRet
= mrStrm
.WriteBytes(pData
, nBytes
);
258 void XclExpStream::WriteZeroBytes( std::size_t nBytes
)
262 std::size_t nBytesLeft
= nBytes
;
263 while( nBytesLeft
> 0 )
265 std::size_t nWriteLen
= ::std::min
< std::size_t >( PrepareWrite(), nBytesLeft
);
266 WriteRawZeroBytes( nWriteLen
);
267 nBytesLeft
-= nWriteLen
;
268 UpdateSizeVars( nWriteLen
);
272 WriteRawZeroBytes( nBytes
);
275 void XclExpStream::WriteZeroBytesToRecord( std::size_t nBytes
)
281 for (std::size_t i
= 0; i
< nBytes
; ++i
)
282 *this << sal_uInt8(0)/*nZero*/;
285 void XclExpStream::CopyFromStream(SvStream
& rInStrm
, sal_uInt64
const nBytes
)
287 sal_uInt64
const nRemaining(rInStrm
.remainingSize());
288 sal_uInt64 nBytesLeft
= ::std::min(nBytes
, nRemaining
);
289 if( nBytesLeft
<= 0 )
292 const std::size_t nMaxBuffer
= 4096;
293 std::unique_ptr
<sal_uInt8
[]> pBuffer(
294 new sal_uInt8
[ ::std::min
<std::size_t>(nBytesLeft
, nMaxBuffer
) ]);
297 while( bValid
&& (nBytesLeft
> 0) )
299 std::size_t nWriteLen
= ::std::min
<std::size_t>(nBytesLeft
, nMaxBuffer
);
300 rInStrm
.ReadBytes(pBuffer
.get(), nWriteLen
);
301 std::size_t nWriteRet
= Write( pBuffer
.get(), nWriteLen
);
302 bValid
= (nWriteLen
== nWriteRet
);
303 nBytesLeft
-= nWriteRet
;
307 void XclExpStream::WriteUnicodeBuffer( const ScfUInt16Vec
& rBuffer
, sal_uInt8 nFlags
)
310 nFlags
&= EXC_STRF_16BIT
; // repeat only 16bit flag
311 sal_uInt16 nCharLen
= nFlags
? 2 : 1;
313 for( const auto& rItem
: rBuffer
)
315 if( mbInRec
&& (mnCurrSize
+ nCharLen
> mnCurrMaxSize
) )
318 operator<<( nFlags
);
323 operator<<( static_cast< sal_uInt8
>( rItem
) );
327 // Xcl has an obscure sense of whether starting a new record or not,
328 // and crashes if it encounters the string header at the very end of a record.
329 // Thus we add 1 to give some room, seems like they do it that way but with another count (10?)
330 void XclExpStream::WriteByteString( const OString
& rString
)
333 std::size_t nLen
= ::std::min
< std::size_t >( rString
.getLength(), 0x00FF );
334 nLen
= ::std::min
< std::size_t >( nLen
, 0xFF );
336 sal_uInt16 nLeft
= PrepareWrite();
337 if( mbInRec
&& (nLeft
<= 1) )
340 operator<<( static_cast< sal_uInt8
>( nLen
) );
341 Write( rString
.getStr(), nLen
);
344 void XclExpStream::WriteCharBuffer( const ScfUInt8Vec
& rBuffer
)
347 Write( rBuffer
.data(), rBuffer
.size() );
350 void XclExpStream::SetEncrypter( XclExpEncrypterRef
const & xEncrypter
)
352 mxEncrypter
= xEncrypter
;
355 bool XclExpStream::HasValidEncrypter() const
357 return mxEncrypter
&& mxEncrypter
->IsValid();
360 void XclExpStream::EnableEncryption( bool bEnable
)
362 mbUseEncrypter
= bEnable
&& HasValidEncrypter();
365 void XclExpStream::DisableEncryption()
367 EnableEncryption(false);
370 void XclExpStream::SetSvStreamPos(sal_uInt64
const nPos
)
372 OSL_ENSURE( !mbInRec
, "XclExpStream::SetSvStreamPos - not allowed inside of a record" );
373 mbInRec
? 0 : mrStrm
.Seek( nPos
);
376 // private --------------------------------------------------------------------
378 void XclExpStream::InitRecord( sal_uInt16 nRecId
)
380 mrStrm
.Seek( STREAM_SEEK_TO_END
);
381 mrStrm
.WriteUInt16( nRecId
);
383 mnLastSizePos
= mrStrm
.Tell();
384 mnHeaderSize
= static_cast< sal_uInt16
>( ::std::min
< std::size_t >( mnPredictSize
, mnCurrMaxSize
) );
385 mrStrm
.WriteUInt16( mnHeaderSize
);
386 mnCurrSize
= mnSliceSize
= 0;
389 void XclExpStream::UpdateRecSize()
391 if( mnCurrSize
!= mnHeaderSize
)
393 mrStrm
.Seek( mnLastSizePos
);
394 mrStrm
.WriteUInt16( mnCurrSize
);
398 void XclExpStream::UpdateSizeVars( std::size_t nSize
)
400 OSL_ENSURE( mnCurrSize
+ nSize
<= mnCurrMaxSize
, "XclExpStream::UpdateSizeVars - record overwritten" );
401 mnCurrSize
= mnCurrSize
+ static_cast< sal_uInt16
>( nSize
);
403 if( mnMaxSliceSize
> 0 )
405 OSL_ENSURE( mnSliceSize
+ nSize
<= mnMaxSliceSize
, "XclExpStream::UpdateSizeVars - slice overwritten" );
406 mnSliceSize
= mnSliceSize
+ static_cast< sal_uInt16
>( nSize
);
407 if( mnSliceSize
>= mnMaxSliceSize
)
412 void XclExpStream::StartContinue()
415 mnCurrMaxSize
= mnMaxContSize
;
416 mnPredictSize
-= mnCurrSize
;
417 InitRecord( EXC_ID_CONT
);
420 void XclExpStream::PrepareWrite( sal_uInt16 nSize
)
424 if( (mnCurrSize
+ nSize
> mnCurrMaxSize
) ||
425 ((mnMaxSliceSize
> 0) && (mnSliceSize
== 0) && (mnCurrSize
+ mnMaxSliceSize
> mnCurrMaxSize
)) )
427 UpdateSizeVars( nSize
);
431 sal_uInt16
XclExpStream::PrepareWrite()
436 if( (mnCurrSize
>= mnCurrMaxSize
) ||
437 ((mnMaxSliceSize
> 0) && (mnSliceSize
== 0) && (mnCurrSize
+ mnMaxSliceSize
> mnCurrMaxSize
)) )
441 nRet
= (mnMaxSliceSize
> 0) ? (mnMaxSliceSize
- mnSliceSize
) : (mnCurrMaxSize
- mnCurrSize
);
446 void XclExpStream::WriteRawZeroBytes( std::size_t nBytes
)
448 const sal_uInt32 nData
= 0;
449 std::size_t nBytesLeft
= nBytes
;
450 while( nBytesLeft
>= sizeof( nData
) )
452 mrStrm
.WriteUInt32( nData
);
453 nBytesLeft
-= sizeof( nData
);
456 mrStrm
.WriteBytes(&nData
, nBytesLeft
);
459 XclExpBiff8Encrypter::XclExpBiff8Encrypter( const XclExpRoot
& rRoot
) :
460 mnOldPos(STREAM_SEEK_TO_END
),
463 Sequence
< NamedValue
> aEncryptionData
= rRoot
.GetEncryptionData();
464 if( !aEncryptionData
.hasElements() )
465 // Empty password. Get the default biff8 password.
466 aEncryptionData
= XclExpRoot::GenerateDefaultEncryptionData();
467 Init( aEncryptionData
);
470 XclExpBiff8Encrypter::~XclExpBiff8Encrypter()
474 void XclExpBiff8Encrypter::GetSaltDigest( sal_uInt8 pnSaltDigest
[16] ) const
476 if ( sizeof( mpnSaltDigest
) == 16 )
477 memcpy( pnSaltDigest
, mpnSaltDigest
, 16 );
480 void XclExpBiff8Encrypter::GetSalt( sal_uInt8 pnSalt
[16] ) const
482 if ( sizeof( mpnSalt
) == 16 )
483 memcpy( pnSalt
, mpnSalt
, 16 );
486 void XclExpBiff8Encrypter::GetDocId( sal_uInt8 pnDocId
[16] ) const
488 if ( sizeof( mpnDocId
) == 16 )
489 memcpy( pnDocId
, mpnDocId
, 16 );
492 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_uInt8 nData
)
494 vector
<sal_uInt8
> aByte
{ nData
};
495 EncryptBytes(rStrm
, aByte
);
498 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_uInt16 nData
)
500 ::std::vector
<sal_uInt8
> pnBytes
502 o3tl::narrowing
<sal_uInt8
>(nData
& 0xFF),
503 o3tl::narrowing
<sal_uInt8
>((nData
>> 8) & 0xFF)
505 EncryptBytes(rStrm
, pnBytes
);
508 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_uInt32 nData
)
510 ::std::vector
<sal_uInt8
> pnBytes
512 o3tl::narrowing
<sal_uInt8
>(nData
& 0xFF),
513 o3tl::narrowing
<sal_uInt8
>((nData
>> 8) & 0xFF),
514 o3tl::narrowing
<sal_uInt8
>((nData
>> 16) & 0xFF),
515 o3tl::narrowing
<sal_uInt8
>((nData
>> 24) & 0xFF)
517 EncryptBytes(rStrm
, pnBytes
);
520 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, float fValue
)
522 ::std::vector
<sal_uInt8
> pnBytes(4);
523 memcpy(pnBytes
.data(), &fValue
, 4);
524 EncryptBytes(rStrm
, pnBytes
);
527 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, double fValue
)
529 ::std::vector
<sal_uInt8
> pnBytes(8);
530 memcpy(pnBytes
.data(), &fValue
, 8);
531 EncryptBytes(rStrm
, pnBytes
);
534 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_Int8 nData
)
536 Encrypt(rStrm
, static_cast<sal_uInt8
>(nData
));
539 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_Int16 nData
)
541 Encrypt(rStrm
, static_cast<sal_uInt16
>(nData
));
544 void XclExpBiff8Encrypter::Encrypt( SvStream
& rStrm
, sal_Int32 nData
)
546 Encrypt(rStrm
, static_cast<sal_uInt32
>(nData
));
549 void XclExpBiff8Encrypter::Init( const Sequence
< NamedValue
>& rEncryptionData
)
553 if( !maCodec
.InitCodec( rEncryptionData
) )
556 maCodec
.GetDocId( mpnDocId
);
558 // generate the salt here
559 rtlRandomPool aRandomPool
= rtl_random_createPool ();
560 rtl_random_getBytes( aRandomPool
, mpnSalt
, 16 );
561 rtl_random_destroyPool( aRandomPool
);
563 memset( mpnSaltDigest
, 0, sizeof( mpnSaltDigest
) );
565 // generate salt hash.
566 ::msfilter::MSCodec_Std97 aCodec
;
567 aCodec
.InitCodec( rEncryptionData
);
568 aCodec
.CreateSaltDigest( mpnSalt
, mpnSaltDigest
);
570 // verify to make sure it's in good shape.
571 mbValid
= maCodec
.VerifyKey( mpnSalt
, mpnSaltDigest
);
574 sal_uInt32
XclExpBiff8Encrypter::GetBlockPos( std::size_t nStrmPos
)
576 return static_cast< sal_uInt32
>( nStrmPos
/ EXC_ENCR_BLOCKSIZE
);
579 sal_uInt16
XclExpBiff8Encrypter::GetOffsetInBlock( std::size_t nStrmPos
)
581 return static_cast< sal_uInt16
>( nStrmPos
% EXC_ENCR_BLOCKSIZE
);
584 void XclExpBiff8Encrypter::EncryptBytes( SvStream
& rStrm
, vector
<sal_uInt8
>& aBytes
)
586 sal_uInt64 nStrmPos
= rStrm
.Tell();
587 sal_uInt16 nBlockOffset
= GetOffsetInBlock(nStrmPos
);
588 sal_uInt32 nBlockPos
= GetBlockPos(nStrmPos
);
590 #if DEBUG_XL_ENCRYPTION
591 fprintf(stdout
, "XclExpBiff8Encrypter::EncryptBytes: stream pos = %ld offset in block = %d block pos = %ld\n",
592 nStrmPos
, nBlockOffset
, nBlockPos
);
595 sal_uInt16 nSize
= static_cast< sal_uInt16
>( aBytes
.size() );
599 #if DEBUG_XL_ENCRYPTION
600 fprintf(stdout
, "RAW: ");
601 for (sal_uInt16 i
= 0; i
< nSize
; ++i
)
602 fprintf(stdout
, "%2.2X ", aBytes
[i
]);
603 fprintf(stdout
, "\n");
606 if (mnOldPos
!= nStrmPos
)
608 sal_uInt16 nOldOffset
= GetOffsetInBlock(mnOldPos
);
609 sal_uInt32 nOldBlockPos
= GetBlockPos(mnOldPos
);
611 if ( (nBlockPos
!= nOldBlockPos
) || (nBlockOffset
< nOldOffset
) )
613 maCodec
.InitCipher(nBlockPos
);
617 if (nBlockOffset
> nOldOffset
)
618 maCodec
.Skip(nBlockOffset
- nOldOffset
);
621 sal_uInt16 nBytesLeft
= nSize
;
623 while (nBytesLeft
> 0)
625 sal_uInt16 nBlockLeft
= EXC_ENCR_BLOCKSIZE
- nBlockOffset
;
626 sal_uInt16 nEncBytes
= ::std::min(nBlockLeft
, nBytesLeft
);
628 bool bRet
= maCodec
.Encode(&aBytes
[nPos
], nEncBytes
, &aBytes
[nPos
], nEncBytes
);
629 OSL_ENSURE(bRet
, "XclExpBiff8Encrypter::EncryptBytes: encryption failed!!");
631 std::size_t nRet
= rStrm
.WriteBytes(&aBytes
[nPos
], nEncBytes
);
632 OSL_ENSURE(nRet
== nEncBytes
, "XclExpBiff8Encrypter::EncryptBytes: fail to write to stream!!");
634 nStrmPos
= rStrm
.Tell();
635 nBlockOffset
= GetOffsetInBlock(nStrmPos
);
636 nBlockPos
= GetBlockPos(nStrmPos
);
637 if (nBlockOffset
== 0)
638 maCodec
.InitCipher(nBlockPos
);
640 nBytesLeft
-= nEncBytes
;
646 static const char* lcl_GetErrorString( FormulaError nScErrCode
)
648 sal_uInt8 nXclErrCode
= XclTools::GetXclErrorCode( nScErrCode
);
649 switch( nXclErrCode
)
651 case EXC_ERR_NULL
: return "#NULL!";
652 case EXC_ERR_DIV0
: return "#DIV/0!";
653 case EXC_ERR_VALUE
: return "#VALUE!";
654 case EXC_ERR_REF
: return "#REF!";
655 case EXC_ERR_NAME
: return "#NAME?";
656 case EXC_ERR_NUM
: return "#NUM!";
658 default: return "#N/A";
662 void XclXmlUtils::GetFormulaTypeAndValue( ScFormulaCell
& rCell
, const char*& rsType
, OUString
& rsValue
)
664 sc::FormulaResultValue aResValue
= rCell
.GetResult();
666 switch (aResValue
.meType
)
668 case sc::FormulaResultValue::Error
:
670 rsValue
= ToOUString(lcl_GetErrorString(aResValue
.mnError
));
672 case sc::FormulaResultValue::Value
:
673 rsType
= rCell
.GetFormatType() == SvNumFormatType::LOGICAL
674 && (aResValue
.mfValue
== 0.0 || aResValue
.mfValue
== 1.0)
677 rsValue
= OUString::number(aResValue
.mfValue
);
679 case sc::FormulaResultValue::String
:
681 rsValue
= rCell
.GetString().getString();
683 case sc::FormulaResultValue::Invalid
:
685 // TODO : double-check this to see if this is correct.
686 rsType
= "inlineStr";
687 rsValue
= rCell
.GetString().getString();
691 OUString
XclXmlUtils::GetStreamName( const char* sStreamDir
, const char* sStream
, sal_Int32 nId
)
695 sBuf
.appendAscii( sStreamDir
);
696 sBuf
.appendAscii( sStream
);
699 if( strstr(sStream
, "vml") )
700 sBuf
.append( ".vml" );
702 sBuf
.append( ".xml" );
703 return sBuf
.makeStringAndClear();
706 OString
XclXmlUtils::ToOString( const Color
& rColor
)
709 sprintf( buf
, "%.2X%.2X%.2X%.2X", rColor
.GetAlpha(), rColor
.GetRed(), rColor
.GetGreen(), rColor
.GetBlue() );
714 OStringBuffer
& XclXmlUtils::ToOString( OStringBuffer
& s
, const ScAddress
& rAddress
)
716 rAddress
.Format(s
, ScRefFlags::VALID
, nullptr, ScAddress::Details( FormulaGrammar::CONV_XL_A1
));
720 OString
XclXmlUtils::ToOString( const ScfUInt16Vec
& rBuffer
)
725 const sal_uInt16
* pBuffer
= rBuffer
.data();
727 reinterpret_cast<sal_Unicode
const *>(pBuffer
), rBuffer
.size(),
728 RTL_TEXTENCODING_UTF8
);
731 OString
XclXmlUtils::ToOString( const ScDocument
& rDoc
, const ScRange
& rRange
, bool bFullAddressNotation
)
733 OUString
sRange(rRange
.Format( rDoc
, ScRefFlags::VALID
,
734 ScAddress::Details( FormulaGrammar::CONV_XL_A1
),
735 bFullAddressNotation
) );
736 return sRange
.toUtf8();
739 OString
XclXmlUtils::ToOString( const ScDocument
& rDoc
, const ScRangeList
& rRangeList
)
742 rRangeList
.Format(s
, ScRefFlags::VALID
, rDoc
, FormulaGrammar::CONV_XL_OOX
, ' ');
746 static ScAddress
lcl_ToAddress( const XclAddress
& rAddress
)
748 return ScAddress( rAddress
.mnCol
, rAddress
.mnRow
, 0 );
751 OStringBuffer
& XclXmlUtils::ToOString( OStringBuffer
& s
, const XclAddress
& rAddress
)
753 return ToOString( s
, lcl_ToAddress( rAddress
));
756 OString
XclXmlUtils::ToOString( const XclExpString
& s
)
758 OSL_ENSURE( !s
.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
759 return ToOString( s
.GetUnicodeBuffer() );
762 static ScRange
lcl_ToRange( const XclRange
& rRange
)
766 aRange
.aStart
= lcl_ToAddress( rRange
.maFirst
);
767 aRange
.aEnd
= lcl_ToAddress( rRange
.maLast
);
772 OString
XclXmlUtils::ToOString( const ScDocument
& rDoc
, const XclRangeList
& rRanges
)
775 for( const auto& rRange
: rRanges
)
777 aRanges
.push_back( lcl_ToRange( rRange
) );
779 return ToOString( rDoc
, aRanges
);
782 OUString
XclXmlUtils::ToOUString( const char* s
)
784 return OUString( s
, static_cast<sal_Int32
>(strlen( s
)), RTL_TEXTENCODING_ASCII_US
);
787 OUString
XclXmlUtils::ToOUString( const ScfUInt16Vec
& rBuf
, sal_Int32 nStart
, sal_Int32 nLength
)
789 if( nLength
== -1 || ( nLength
> (static_cast<sal_Int32
>(rBuf
.size()) - nStart
) ) )
790 nLength
= (rBuf
.size() - nStart
);
794 reinterpret_cast<sal_Unicode
const *>(&rBuf
[nStart
]), nLength
)
798 OUString
XclXmlUtils::ToOUString(
799 sc::CompileFormulaContext
& rCtx
, const ScAddress
& rAddress
, const ScTokenArray
* pTokenArray
,
800 FormulaError nErrCode
)
802 ScCompiler
aCompiler( rCtx
, rAddress
, const_cast<ScTokenArray
&>(*pTokenArray
));
804 /* TODO: isn't this the same as passed in rCtx and thus superfluous? */
805 aCompiler
.SetGrammar(FormulaGrammar::GRAM_OOXML
);
807 sal_Int32 nLen
= pTokenArray
->GetLen();
808 OUStringBuffer
aBuffer( nLen
? (nLen
* 5) : 8 );
810 aCompiler
.CreateStringFromTokenArray( aBuffer
);
813 if (nErrCode
!= FormulaError::NONE
)
814 aCompiler
.AppendErrorConstant( aBuffer
, nErrCode
);
817 // No code SHOULD be an "error cell", assert caller thought of that
819 assert(!"No code and no error.");
823 return aBuffer
.makeStringAndClear();
826 OUString
XclXmlUtils::ToOUString( const XclExpString
& s
)
828 OSL_ENSURE( !s
.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
829 return ToOUString( s
.GetUnicodeBuffer() );
832 static void lcl_WriteValue( const sax_fastparser::FSHelperPtr
& rStream
, sal_Int32 nElement
, const char* pValue
)
836 rStream
->singleElement(nElement
, XML_val
, pValue
);
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 lcl_WriteValue( pStream
, XML_b
, rFontData
.mnWeight
> 400 ? ToPsz( true ) : nullptr );
872 lcl_WriteValue( pStream
, XML_i
, rFontData
.mbItalic
? ToPsz( true ) : nullptr );
873 lcl_WriteValue( pStream
, XML_strike
, rFontData
.mbStrikeout
? ToPsz( true ) : nullptr );
874 // OOXTODO: lcl_WriteValue( rStream, XML_condense, ); // mac compatibility setting
875 // OOXTODO: lcl_WriteValue( rStream, XML_extend, ); // compatibility setting
876 lcl_WriteValue( pStream
, XML_outline
, rFontData
.mbOutline
? ToPsz( true ) : nullptr );
877 lcl_WriteValue( pStream
, XML_shadow
, rFontData
.mbShadow
? ToPsz( true ) : nullptr );
878 lcl_WriteValue( pStream
, XML_u
, bHaveUnderline
? pUnderline
: nullptr );
879 lcl_WriteValue( pStream
, XML_vertAlign
, bHaveVertAlign
? pVertAlign
: nullptr );
880 lcl_WriteValue( pStream
, XML_sz
, OString::number( rFontData
.mnHeight
/ 20.0 ).getStr() ); // Twips->Pt
881 if( rFontData
.maColor
!= Color( ColorAlpha
, 0, 0xFF, 0xFF, 0xFF ) )
882 pStream
->singleElement( XML_color
,
883 // OOXTODO: XML_auto, bool
884 // OOXTODO: XML_indexed, uint
885 XML_rgb
, XclXmlUtils::ToOString(rFontData
.maColor
)
886 // OOXTODO: XML_theme, index into <clrScheme/>
887 // OOXTODO: XML_tint, double
889 lcl_WriteValue( pStream
, nFontId
, rFontData
.maName
.toUtf8().getStr() );
890 lcl_WriteValue( pStream
, XML_family
, OString::number( rFontData
.mnFamily
).getStr() );
891 lcl_WriteValue( pStream
, XML_charset
, rFontData
.mnCharSet
!= 0 ? OString::number( rFontData
.mnCharSet
).getStr() : nullptr );
896 XclExpXmlStream::XclExpXmlStream( const uno::Reference
< XComponentContext
>& rCC
, bool bExportVBA
, bool bExportTemplate
)
897 : XmlFilterBase( rCC
),
899 mbExportVBA(bExportVBA
),
900 mbExportTemplate(bExportTemplate
)
904 XclExpXmlStream::~XclExpXmlStream()
906 assert(maStreams
.empty() && "Forgotten PopStream()?");
909 sax_fastparser::FSHelperPtr
& XclExpXmlStream::GetCurrentStream()
911 OSL_ENSURE( !maStreams
.empty(), "XclExpXmlStream::GetCurrentStream - no current stream" );
912 return maStreams
.top();
915 void XclExpXmlStream::PushStream( sax_fastparser::FSHelperPtr
const & aStream
)
917 maStreams
.push( aStream
);
920 void XclExpXmlStream::PopStream()
922 OSL_ENSURE( !maStreams
.empty(), "XclExpXmlStream::PopStream - stack is empty!" );
926 sax_fastparser::FSHelperPtr
XclExpXmlStream::GetStreamForPath( const OUString
& sPath
)
928 if( maOpenedStreamMap
.find( sPath
) == maOpenedStreamMap
.end() )
929 return sax_fastparser::FSHelperPtr();
930 return maOpenedStreamMap
[ sPath
].second
;
933 void XclExpXmlStream::WriteAttribute(sal_Int32 nAttr
, std::u16string_view sVal
)
935 GetCurrentStream()->write(" ")->writeId(nAttr
)->write("=\"")->writeEscaped(sVal
)->write("\"");
938 sax_fastparser::FSHelperPtr
XclExpXmlStream::CreateOutputStream (
939 const OUString
& sFullStream
,
940 std::u16string_view sRelativeStream
,
941 const uno::Reference
< XOutputStream
>& xParentRelation
,
942 const char* sContentType
,
943 std::u16string_view sRelationshipType
,
944 OUString
* pRelationshipId
)
946 OUString sRelationshipId
;
947 if (xParentRelation
.is())
948 sRelationshipId
= addRelation( xParentRelation
, OUString(sRelationshipType
), sRelativeStream
);
950 sRelationshipId
= addRelation( OUString(sRelationshipType
), sRelativeStream
);
952 if( pRelationshipId
)
953 *pRelationshipId
= sRelationshipId
;
955 sax_fastparser::FSHelperPtr p
= openFragmentStreamWithSerializer( sFullStream
, OUString::createFromAscii( sContentType
) );
957 maOpenedStreamMap
[ sFullStream
] = std::make_pair( sRelationshipId
, p
);
962 bool XclExpXmlStream::importDocument() noexcept
967 oox::vml::Drawing
* XclExpXmlStream::getVmlDrawing()
972 const oox::drawingml::Theme
* XclExpXmlStream::getCurrentTheme() const
977 oox::drawingml::table::TableStyleListPtr
XclExpXmlStream::getTableStyles()
979 return oox::drawingml::table::TableStyleListPtr();
982 oox::drawingml::chart::ChartConverter
* XclExpXmlStream::getChartConverter()
988 ScDocShell
* XclExpXmlStream::getDocShell()
990 uno::Reference
< XInterface
> xModel( getModel(), UNO_QUERY
);
992 ScModelObj
*pObj
= dynamic_cast < ScModelObj
* >( xModel
.get() );
995 return static_cast < ScDocShell
* >( pObj
->GetEmbeddedObject() );
1000 bool XclExpXmlStream::exportDocument()
1002 ScDocShell
* pShell
= getDocShell();
1003 ScDocument
& rDoc
= pShell
->GetDocument();
1004 ScRefreshTimerProtector
aProt(rDoc
.GetRefreshTimerControlAddress());
1006 const bool bValidateTabNames
= officecfg::Office::Calc::Filter::Export::MS_Excel::TruncateLongSheetNames::get();
1007 std::vector
<OUString
> aOriginalTabNames
;
1008 if (bValidateTabNames
)
1010 validateTabNames(aOriginalTabNames
);
1013 uno::Reference
<task::XStatusIndicator
> xStatusIndicator
= getStatusIndicator();
1015 if (xStatusIndicator
.is())
1016 xStatusIndicator
->start(ScResId(STR_SAVE_DOC
), 100);
1018 // NOTE: Don't use SotStorage or SvStream any more, and never call
1019 // SfxMedium::GetOutStream() anywhere in the xlsx export filter code!
1020 // Instead, write via XOutputStream instance.
1021 tools::SvRef
<SotStorage
> rStorage
= static_cast<SotStorage
*>(nullptr);
1022 drawingml::DrawingML::ResetMlCounters();
1024 XclExpRootData
aData(
1025 EXC_BIFF8
, *pShell
->GetMedium (), rStorage
, rDoc
,
1026 msfilter::util::getBestTextEncodingFromLocale(
1027 Application::GetSettings().GetLanguageTag().getLocale()));
1028 aData
.meOutput
= EXC_OUTPUT_XML_2007
;
1029 aData
.maXclMaxPos
.Set( EXC_MAXCOL_XML_2007
, EXC_MAXROW_XML_2007
, EXC_MAXTAB_XML_2007
);
1030 aData
.maMaxPos
.SetCol( ::std::min( aData
.maScMaxPos
.Col(), aData
.maXclMaxPos
.Col() ) );
1031 aData
.maMaxPos
.SetRow( ::std::min( aData
.maScMaxPos
.Row(), aData
.maXclMaxPos
.Row() ) );
1032 aData
.maMaxPos
.SetTab( ::std::min( aData
.maScMaxPos
.Tab(), aData
.maXclMaxPos
.Tab() ) );
1033 aData
.mpCompileFormulaCxt
= std::make_shared
<sc::CompileFormulaContext
>(rDoc
);
1034 // set target path to get correct relative links to target document, not source
1035 INetURLObject
aPath(getFileUrl());
1036 aData
.maBasePath
= OUString("file:///" + aPath
.GetPath() + "\\").replace('\\', '/')
1038 .replaceFirst("file:////", "file:///");
1040 XclExpRoot
aRoot( aData
);
1043 aRoot
.GetOldRoot().pER
= &aRoot
;
1044 aRoot
.GetOldRoot().eDateiTyp
= Biff8
;
1045 // Get the viewsettings before processing
1046 if( ScDocShell::GetViewData() )
1047 ScDocShell::GetViewData()->WriteExtOptions( mpRoot
->GetExtDocOptions() );
1049 OUString
const workbook
= "xl/workbook.xml";
1050 const char* pWorkbookContentType
= nullptr;
1053 if (mbExportTemplate
)
1055 pWorkbookContentType
= "application/vnd.ms-excel.template.macroEnabled.main+xml";
1059 pWorkbookContentType
= "application/vnd.ms-excel.sheet.macroEnabled.main+xml";
1064 if (mbExportTemplate
)
1066 pWorkbookContentType
= "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml";
1070 pWorkbookContentType
= "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
1074 PushStream( CreateOutputStream( workbook
, workbook
,
1075 uno::Reference
<XOutputStream
>(),
1076 pWorkbookContentType
,
1077 oox::getRelationship(Relationship::OFFICEDOCUMENT
) ) );
1081 VbaExport
aExport(getModel());
1082 if (aExport
.containsVBAProject())
1084 SvMemoryStream
aVbaStream(4096, 4096);
1085 tools::SvRef
<SotStorage
> pVBAStorage(new SotStorage(aVbaStream
));
1086 aExport
.exportVBA( pVBAStorage
.get() );
1088 css::uno::Reference
<css::io::XInputStream
> xVBAStream(
1089 new utl::OInputStreamWrapper(aVbaStream
));
1090 css::uno::Reference
<css::io::XOutputStream
> xVBAOutput
=
1091 openFragmentStream("xl/vbaProject.bin", "application/vnd.ms-office.vbaProject");
1092 comphelper::OStorageHelper::CopyInputToOutput(xVBAStream
, xVBAOutput
);
1094 addRelation(GetCurrentStream()->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT
), u
"vbaProject.bin");
1098 // destruct at the end of the block
1100 ExcDocument
aDocRoot( aRoot
);
1101 if (xStatusIndicator
.is())
1102 xStatusIndicator
->setValue(10);
1104 if (xStatusIndicator
.is())
1105 xStatusIndicator
->setValue(40);
1106 aDocRoot
.WriteXml( *this );
1107 rDoc
.GetExternalRefManager()->disableSkipUnusedFileIds();
1111 // Free all FSHelperPtr, to flush data before committing storage
1112 maOpenedStreamMap
.clear();
1116 if (bValidateTabNames
)
1118 restoreTabNames(aOriginalTabNames
);
1121 if (xStatusIndicator
.is())
1122 xStatusIndicator
->end();
1127 ::oox::ole::VbaProject
* XclExpXmlStream::implCreateVbaProject() const
1129 return new ::oox::xls::ExcelVbaProject( getComponentContext(), uno::Reference
< XSpreadsheetDocument
>( getModel(), UNO_QUERY
) );
1132 OUString
XclExpXmlStream::getImplementationName()
1137 void XclExpXmlStream::validateTabNames(std::vector
<OUString
>& aOriginalTabNames
)
1139 const int MAX_TAB_NAME_LENGTH
= 31;
1141 ScDocShell
* pShell
= getDocShell();
1142 ScDocument
& rDoc
= pShell
->GetDocument();
1144 // get original names
1145 aOriginalTabNames
.resize(rDoc
.GetTableCount());
1146 for (SCTAB nTab
=0; nTab
< rDoc
.GetTableCount(); nTab
++)
1148 rDoc
.GetName(nTab
, aOriginalTabNames
[nTab
]);
1152 std::vector
<OUString
> aNewTabNames
;
1153 aNewTabNames
.reserve(rDoc
.GetTableCount());
1156 for (SCTAB nTab
=0; nTab
< rDoc
.GetTableCount(); nTab
++)
1158 const OUString
& rOriginalName
= aOriginalTabNames
[nTab
];
1159 if (rOriginalName
.getLength() > MAX_TAB_NAME_LENGTH
)
1163 // let's try just truncate "<first 31 chars>"
1164 if (aNewName
.isEmpty())
1166 aNewName
= rOriginalName
.copy(0, MAX_TAB_NAME_LENGTH
);
1167 if (aNewTabNames
.end() != std::find(aNewTabNames
.begin(), aNewTabNames
.end(), aNewName
) ||
1168 aOriginalTabNames
.end() != std::find(aOriginalTabNames
.begin(), aOriginalTabNames
.end(), aNewName
))
1170 // was found => let's use another tab name
1175 // let's try "<first N chars>-XXX" template
1176 for (int digits
=1; digits
<10 && aNewName
.isEmpty(); digits
++)
1178 const int rangeStart
= pow(10, digits
- 1);
1179 const int rangeEnd
= pow(10, digits
);
1181 for (int i
=rangeStart
; i
<rangeEnd
&& aNewName
.isEmpty(); i
++)
1183 aNewName
= OUString::Concat(rOriginalName
.subView(0, MAX_TAB_NAME_LENGTH
- 1 - digits
)) + "-" + OUString::number(i
);
1184 if (aNewTabNames
.end() != std::find(aNewTabNames
.begin(), aNewTabNames
.end(), aNewName
) ||
1185 aOriginalTabNames
.end() != std::find(aOriginalTabNames
.begin(), aOriginalTabNames
.end(), aNewName
))
1187 // was found => let's use another tab name
1193 if (!aNewName
.isEmpty())
1195 // new name was created => rename
1196 renameTab(nTab
, aNewName
);
1197 aNewTabNames
.push_back(aNewName
);
1201 // default: do not rename
1202 aNewTabNames
.push_back(rOriginalName
);
1207 // default: do not rename
1208 aNewTabNames
.push_back(rOriginalName
);
1213 void XclExpXmlStream::restoreTabNames(const std::vector
<OUString
>& aOriginalTabNames
)
1215 ScDocShell
* pShell
= getDocShell();
1216 ScDocument
& rDoc
= pShell
->GetDocument();
1218 for (SCTAB nTab
=0; nTab
< rDoc
.GetTableCount(); nTab
++)
1220 const OUString
& rOriginalName
= aOriginalTabNames
[nTab
];
1222 OUString rModifiedName
;
1223 rDoc
.GetName(nTab
, rModifiedName
);
1225 if (rOriginalName
!= rModifiedName
)
1227 renameTab(nTab
, rOriginalName
);
1232 void XclExpXmlStream::renameTab(SCTAB aTab
, OUString aNewName
)
1234 ScDocShell
* pShell
= getDocShell();
1235 ScDocument
& rDoc
= pShell
->GetDocument();
1237 bool bAutoCalcShellDisabled
= rDoc
.IsAutoCalcShellDisabled();
1238 bool bIdleEnabled
= rDoc
.IsIdleEnabled();
1240 rDoc
.SetAutoCalcShellDisabled( true );
1241 rDoc
.EnableIdle(false);
1243 if (rDoc
.RenameTab(aTab
, aNewName
))
1245 SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScTablesChanged
));
1248 rDoc
.SetAutoCalcShellDisabled( bAutoCalcShellDisabled
);
1249 rDoc
.EnableIdle(bIdleEnabled
);
1252 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */