use insert function instead of for loop
[LibreOffice.git] / sc / source / filter / excel / xestream.cxx
blobd9e7a6cf57ea1b35d9a810dac3768511962c6f91
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <stdio.h>
21 #include <string.h>
22 #include <utility>
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>
39 #include <docuno.hxx>
40 #include <xestream.hxx>
41 #include <xladdress.hxx>
42 #include <xlstring.hxx>
43 #include <xltools.hxx>
44 #include <xeroot.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>
55 #include <root.hxx>
56 #include <sfx2/app.hxx>
58 #include <docsh.hxx>
59 #include <tabvwsh.hxx>
60 #include <viewdata.hxx>
61 #include <excdoc.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>
73 #include <memory>
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;
82 using ::std::vector;
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 ) :
94 mrStrm( rOutStrm ),
95 mrRoot( rRoot ),
96 mbUseEncrypter( false ),
97 mnMaxRecSize( nMaxRecSize ),
98 mnCurrMaxSize( 0 ),
99 mnMaxSliceSize( 0 ),
100 mnHeaderSize( 0 ),
101 mnCurrSize( 0 ),
102 mnSliceSize( 0 ),
103 mnPredictSize( 0 ),
104 mnLastSizePos( 0 ),
105 mbInRec( false )
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" );
120 DisableEncryption();
121 mnMaxContSize = mnCurrMaxSize = mnMaxRecSize;
122 mnPredictSize = nRecSize;
123 mbInRec = true;
124 InitRecord( nRecId );
125 SetSliceSize( 0 );
126 EnableEncryption();
129 void XclExpStream::EndRecord()
131 OSL_ENSURE( mbInRec, "XclExpStream::EndRecord - no record open" );
132 DisableEncryption();
133 UpdateRecSize();
134 mrStrm.Seek( STREAM_SEEK_TO_END );
135 mbInRec = false;
138 void XclExpStream::SetSliceSize( sal_uInt16 nSize )
140 mnMaxSliceSize = nSize;
141 mnSliceSize = 0;
144 XclExpStream& XclExpStream::operator<<( sal_Int8 nValue )
146 PrepareWrite( 1 );
147 if (mbUseEncrypter && HasValidEncrypter())
148 mxEncrypter->Encrypt(mrStrm, nValue);
149 else
150 mrStrm.WriteSChar( nValue );
151 return *this;
154 XclExpStream& XclExpStream::operator<<( sal_uInt8 nValue )
156 PrepareWrite( 1 );
157 if (mbUseEncrypter && HasValidEncrypter())
158 mxEncrypter->Encrypt(mrStrm, nValue);
159 else
160 mrStrm.WriteUChar( nValue );
161 return *this;
164 XclExpStream& XclExpStream::operator<<( sal_Int16 nValue )
166 PrepareWrite( 2 );
167 if (mbUseEncrypter && HasValidEncrypter())
168 mxEncrypter->Encrypt(mrStrm, nValue);
169 else
170 mrStrm.WriteInt16( nValue );
171 return *this;
174 XclExpStream& XclExpStream::operator<<( sal_uInt16 nValue )
176 PrepareWrite( 2 );
177 if (mbUseEncrypter && HasValidEncrypter())
178 mxEncrypter->Encrypt(mrStrm, nValue);
179 else
180 mrStrm.WriteUInt16( nValue );
181 return *this;
184 XclExpStream& XclExpStream::operator<<( sal_Int32 nValue )
186 PrepareWrite( 4 );
187 if (mbUseEncrypter && HasValidEncrypter())
188 mxEncrypter->Encrypt(mrStrm, nValue);
189 else
190 mrStrm.WriteInt32( nValue );
191 return *this;
194 XclExpStream& XclExpStream::operator<<( sal_uInt32 nValue )
196 PrepareWrite( 4 );
197 if (mbUseEncrypter && HasValidEncrypter())
198 mxEncrypter->Encrypt(mrStrm, nValue);
199 else
200 mrStrm.WriteUInt32( nValue );
201 return *this;
204 XclExpStream& XclExpStream::operator<<( float fValue )
206 PrepareWrite( 4 );
207 if (mbUseEncrypter && HasValidEncrypter())
208 mxEncrypter->Encrypt(mrStrm, fValue);
209 else
210 mrStrm.WriteFloat( fValue );
211 return *this;
214 XclExpStream& XclExpStream::operator<<( double fValue )
216 PrepareWrite( 8 );
217 if (mbUseEncrypter && HasValidEncrypter())
218 mxEncrypter->Encrypt(mrStrm, fValue);
219 else
220 mrStrm.WriteDouble( fValue );
221 return *this;
224 std::size_t XclExpStream::Write( const void* pData, std::size_t nBytes )
226 std::size_t nRet = 0;
227 if( pData && (nBytes > 0) )
229 if( mbInRec )
231 const sal_uInt8* pBuffer = static_cast< const sal_uInt8* >( pData );
232 std::size_t nBytesLeft = nBytes;
233 bool bValid = true;
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 ?
247 else
249 nWriteRet = mrStrm.WriteBytes(pBuffer, nWriteLen);
250 bValid = (nWriteLen == nWriteRet);
251 OSL_ENSURE( bValid, "XclExpStream::Write - stream write error" );
253 pBuffer += nWriteRet;
254 nRet += nWriteRet;
255 nBytesLeft -= nWriteRet;
256 UpdateSizeVars( nWriteRet );
259 else
260 nRet = mrStrm.WriteBytes(pData, nBytes);
262 return nRet;
265 void XclExpStream::WriteZeroBytes( std::size_t nBytes )
267 if( mbInRec )
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 );
278 else
279 WriteRawZeroBytes( nBytes );
282 void XclExpStream::WriteZeroBytesToRecord( std::size_t nBytes )
284 if (!mbInRec)
285 // not in record.
286 return;
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 )
297 return;
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) ]);
302 bool bValid = true;
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 )
316 SetSliceSize( 0 );
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) )
324 StartContinue();
325 operator<<( nFlags );
327 if( nCharLen == 2 )
328 operator<<( rItem );
329 else
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 )
339 SetSliceSize( 0 );
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) )
345 StartContinue();
347 operator<<( static_cast< sal_uInt8 >( nLen ) );
348 Write( rString.getStr(), nLen );
351 void XclExpStream::WriteCharBuffer( const ScfUInt8Vec& rBuffer )
353 SetSliceSize( 0 );
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 )
415 mnSliceSize = 0;
419 void XclExpStream::StartContinue()
421 UpdateRecSize();
422 mnCurrMaxSize = mnMaxContSize;
423 mnPredictSize -= mnCurrSize;
424 InitRecord( EXC_ID_CONT );
427 void XclExpStream::PrepareWrite( sal_uInt16 nSize )
429 if( mbInRec )
431 if( (mnCurrSize + nSize > mnCurrMaxSize) ||
432 ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
433 StartContinue();
434 UpdateSizeVars( nSize );
438 sal_uInt16 XclExpStream::PrepareWrite()
440 sal_uInt16 nRet = 0;
441 if( mbInRec )
443 if( (mnCurrSize >= mnCurrMaxSize) ||
444 ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
445 StartContinue();
446 UpdateSizeVars( 0 );
448 nRet = (mnMaxSliceSize > 0) ? (mnMaxSliceSize - mnSliceSize) : (mnCurrMaxSize - mnCurrSize);
450 return nRet;
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 );
462 if( nBytesLeft )
463 mrStrm.WriteBytes(&nData, nBytesLeft);
466 XclExpBiff8Encrypter::XclExpBiff8Encrypter( const XclExpRoot& rRoot ) :
467 mnOldPos(STREAM_SEEK_TO_END),
468 mbValid(false)
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 )
558 mbValid = false;
560 if( !maCodec.InitCodec( rEncryptionData ) )
561 return;
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() );
603 if (nSize == 0)
604 return;
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");
611 #endif
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);
621 nOldOffset = 0;
624 if (nBlockOffset > nOldOffset)
625 maCodec.Skip(nBlockOffset - nOldOffset);
628 sal_uInt16 nBytesLeft = nSize;
629 sal_uInt16 nPos = 0;
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;
648 nPos += nEncBytes;
650 mnOldPos = nStrmPos;
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!";
664 case EXC_ERR_NA:
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:
676 rsType = "e";
677 rsValue = ToOUString(lcl_GetErrorString(aResValue.mnError));
678 break;
679 case sc::FormulaResultValue::Value:
680 rsType = rCell.GetFormatType() == SvNumFormatType::LOGICAL
681 && (aResValue.mfValue == 0.0 || aResValue.mfValue == 1.0)
682 ? "b"
683 : "n";
684 rsValue = OUString::number(aResValue.mfValue);
685 break;
686 case sc::FormulaResultValue::String:
687 rsType = "str";
688 rsValue = rCell.GetString().getString();
689 break;
690 case sc::FormulaResultValue::Invalid:
691 default:
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 )
700 OUStringBuffer sBuf;
701 if( sStreamDir )
702 sBuf.appendAscii( sStreamDir );
703 sBuf.appendAscii( sStream );
704 if( nId )
705 sBuf.append( nId );
706 if( strstr(sStream, "vml") )
707 sBuf.append( ".vml" );
708 else
709 sBuf.append( ".xml" );
710 return sBuf.makeStringAndClear();
713 OString XclXmlUtils::ToOString( const Color& rColor )
715 char buf[9];
716 o3tl::sprintf( buf, "%.2X%.2X%.2X%.2X", rColor.GetAlpha(), rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
717 buf[8] = '\0';
718 return buf;
721 OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const ScAddress& rAddress )
723 rAddress.Format(s, ScRefFlags::VALID, nullptr, ScAddress::Details( FormulaGrammar::CONV_XL_A1));
724 return s;
727 OString XclXmlUtils::ToOString( const ScfUInt16Vec& rBuffer )
729 if(rBuffer.empty())
730 return OString();
732 const sal_uInt16* pBuffer = rBuffer.data();
733 return OString(
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 )
748 OUString s;
749 rRangeList.Format(s, ScRefFlags::VALID, rDoc, FormulaGrammar::CONV_XL_OOX, ' ');
750 return s.toUtf8();
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 )
771 ScRange aRange;
773 aRange.aStart = lcl_ToAddress( rRange.maFirst );
774 aRange.aEnd = lcl_ToAddress( rRange.maLast );
776 return aRange;
779 OString XclXmlUtils::ToOString( const ScDocument& rDoc, const XclRangeList& rRanges )
781 ScRangeList aRanges;
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);
799 return nLength > 0
800 ? OUString(
801 reinterpret_cast<sal_Unicode const *>(&rBuf[nStart]), nLength)
802 : OUString();
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 );
816 if (nLen)
817 aCompiler.CreateStringFromTokenArray( aBuffer );
818 else
820 if (nErrCode != FormulaError::NONE)
821 aCompiler.AppendErrorConstant( aBuffer, nErrCode);
822 else
824 // No code SHOULD be an "error cell", assert caller thought of that
825 // and it really is.
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;
842 switch( eUnderline )
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";
848 case LINESTYLE_NONE:
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 ));
883 if (bHaveUnderline)
884 pStream->singleElement(XML_u, XML_val, pUnderline);
885 if (bHaveVertAlign)
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));
908 return pStream;
911 XclExpXmlStream::XclExpXmlStream( const uno::Reference< XComponentContext >& rCC, bool bExportVBA, bool bExportTemplate )
912 : XmlFilterBase( rCC ),
913 mpRoot( nullptr ),
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!" );
938 maStreams.pop();
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 );
964 else
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 );
974 return p;
977 bool XclExpXmlStream::importDocument() noexcept
979 return false;
982 oox::vml::Drawing* XclExpXmlStream::getVmlDrawing()
984 return nullptr;
987 const oox::drawingml::Theme* XclExpXmlStream::getCurrentTheme() const
989 return nullptr;
992 oox::drawingml::table::TableStyleListPtr XclExpXmlStream::getTableStyles()
994 return oox::drawingml::table::TableStyleListPtr();
997 oox::drawingml::chart::ChartConverter* XclExpXmlStream::getChartConverter()
999 // DO NOT CALL
1000 return nullptr;
1003 ScDocShell* XclExpXmlStream::getDocShell()
1005 uno::Reference< XInterface > xModel( getModel(), UNO_QUERY );
1007 ScModelObj *pObj = comphelper::getFromUnoTunnel < ScModelObj >( xModel );
1009 if ( pObj )
1010 return static_cast < ScDocShell* >( pObj->GetEmbeddedObject() );
1012 return nullptr;
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('\\', '/')
1056 // fix for Linux
1057 .replaceFirst("file:////", "file:///");
1059 XclExpRoot aRoot( aData );
1061 mpRoot = &aRoot;
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() );
1067 else
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;
1080 if (mbExportVBA)
1082 if (mbExportTemplate)
1084 pWorkbookContentType = "application/vnd.ms-excel.template.macroEnabled.main+xml";
1086 else
1088 pWorkbookContentType = "application/vnd.ms-excel.sheet.macroEnabled.main+xml";
1091 else
1093 if (mbExportTemplate)
1095 pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml";
1097 else
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) ) );
1108 if (mbExportVBA)
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() );
1116 aVbaStream.Seek(0);
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);
1132 aDocRoot.ReadDoc();
1133 if (xStatusIndicator.is())
1134 xStatusIndicator->setValue(40);
1135 aDocRoot.WriteXml( *this );
1136 rDoc.GetExternalRefManager()->disableSkipUnusedFileIds();
1139 PopStream();
1140 // Free all FSHelperPtr, to flush data before committing storage
1141 for (auto& entry : maOpenedStreamMap)
1143 if (!entry.second.second)
1144 continue;
1145 entry.second.second->endDocument();
1147 maOpenedStreamMap.clear();
1149 commitStorage();
1151 if (bValidateTabNames)
1153 restoreTabNames(aOriginalTabNames);
1156 if (xStatusIndicator.is())
1157 xStatusIndicator->end();
1158 mpRoot = nullptr;
1160 rGraphicExportCache.pop();
1162 return true;
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]);
1189 // new tab names
1190 std::vector<OUString> aNewTabNames;
1191 aNewTabNames.reserve(rDoc.GetTableCount());
1193 // check and rename
1194 for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
1196 const OUString& rOriginalName = aOriginalTabNames[nTab];
1197 if (rOriginalName.getLength() > MAX_TAB_NAME_LENGTH)
1199 OUString aNewName;
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
1209 aNewName.clear();
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
1226 aNewName.clear();
1231 if (!aNewName.isEmpty())
1233 // new name was created => rename
1234 renameTab(nTab, aNewName);
1235 aNewTabNames.push_back(aNewName);
1237 else
1239 // default: do not rename
1240 aNewTabNames.push_back(rOriginalName);
1243 else
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: */