bump product version to 4.1.6.2
[LibreOffice.git] / sc / source / filter / excel / xehelper.cxx
blobdc05fdac8635961a091e771b7814e82a7cfb555a
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 <com/sun/star/i18n/XBreakIterator.hpp>
21 #include <com/sun/star/i18n/ScriptType.hpp>
22 #include <sfx2/objsh.hxx>
23 #include <vcl/font.hxx>
24 #include <tools/urlobj.hxx>
25 #include <svl/itemset.hxx>
26 #include <svtools/ctrltool.hxx>
27 #include <svx/svdotext.hxx>
28 #include <editeng/outlobj.hxx>
29 #include "scitems.hxx"
30 #include <editeng/fhgtitem.hxx>
31 #include <editeng/flstitem.hxx>
32 #include <editeng/colritem.hxx>
33 #include <editeng/eeitem.hxx>
34 #include <editeng/flditem.hxx>
35 #include <editeng/escapementitem.hxx>
36 #include <editeng/svxfont.hxx>
38 #include "document.hxx"
39 #include "docpool.hxx"
40 #include "formulacell.hxx"
41 #include "editutil.hxx"
42 #include "patattr.hxx"
43 #include "xestyle.hxx"
44 #include "fprogressbar.hxx"
45 #include "xltracer.hxx"
46 #include "xecontent.hxx"
47 #include "xelink.hxx"
48 #include "xehelper.hxx"
50 using ::com::sun::star::uno::Reference;
51 using ::com::sun::star::i18n::XBreakIterator;
53 // Export progress bar ========================================================
55 XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) :
56 XclExpRoot( rRoot ),
57 mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ),
58 mpSubProgress( 0 ),
59 mpSubRowCreate( 0 ),
60 mpSubRowFinal( 0 ),
61 mnSegRowFinal( SCF_INV_SEGMENT ),
62 mnRowCount( 0 )
66 XclExpProgressBar::~XclExpProgressBar()
70 void XclExpProgressBar::Initialize()
72 const ScDocument& rDoc = GetDoc();
73 const XclExpTabInfo& rTabInfo = GetTabInfo();
74 SCTAB nScTabCount = rTabInfo.GetScTabCount();
76 // *** segment: creation of ROW records *** -------------------------------
78 sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 );
79 mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate );
80 maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT );
82 for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab )
84 if( rTabInfo.IsExportTab( nScTab ) )
86 SCCOL nLastUsedScCol;
87 SCROW nLastUsedScRow;
88 rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
89 sal_Size nSegSize = static_cast< sal_Size >( nLastUsedScRow + 1 );
90 maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize );
94 // *** segment: writing all ROW records *** -------------------------------
96 mnSegRowFinal = mxProgress->AddSegment( 1000 );
97 // sub progress bar and segment are created later in ActivateFinalRowsSegment()
100 void XclExpProgressBar::IncRowRecordCount()
102 ++mnRowCount;
105 void XclExpProgressBar::ActivateCreateRowsSegment()
107 OSL_ENSURE( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
108 "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
109 sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ];
110 OSL_ENSURE( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
111 if( nSeg != SCF_INV_SEGMENT )
113 mpSubProgress = mpSubRowCreate;
114 mpSubProgress->ActivateSegment( nSeg );
116 else
117 mpSubProgress = 0;
120 void XclExpProgressBar::ActivateFinalRowsSegment()
122 if( !mpSubRowFinal && (mnRowCount > 0) )
124 mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal );
125 mpSubRowFinal->AddSegment( mnRowCount );
127 mpSubProgress = mpSubRowFinal;
128 if( mpSubProgress )
129 mpSubProgress->Activate();
132 void XclExpProgressBar::Progress()
134 if( mpSubProgress && !mpSubProgress->IsFull() )
135 mpSubProgress->Progress();
138 // Calc->Excel cell address/range conversion ==================================
140 namespace {
142 /** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
143 inline void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow )
145 rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol );
146 rXclPos.mnRow = static_cast< sal_uInt32 >( nScRow );
149 } // namespace
151 // ----------------------------------------------------------------------------
153 XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) :
154 XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() )
158 // cell address ---------------------------------------------------------------
160 bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn )
162 // ScAddress::operator<=() doesn't do what we want here
163 bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col());
164 bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row());
165 bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab());
167 bool bValid = bValidCol && bValidRow && bValidTab;
168 if( !bValid && bWarn )
170 mbColTrunc |= !bValidCol;
171 mbRowTrunc |= !bValidRow;
172 mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs
173 mrTracer.TraceInvalidAddress( rScPos, maMaxPos );
175 return bValid;
178 bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos,
179 const ScAddress& rScPos, bool bWarn )
181 bool bValid = CheckAddress( rScPos, bWarn );
182 if( bValid )
183 lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() );
184 return bValid;
187 XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn )
189 XclAddress aXclPos( ScAddress::UNINITIALIZED );
190 if( !ConvertAddress( aXclPos, rScPos, bWarn ) )
191 lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) );
192 return aXclPos;
195 // cell range -----------------------------------------------------------------
197 bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn )
199 return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn );
202 bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn )
204 rScRange.Justify();
206 // check start position
207 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
208 if( bValidStart )
210 // check & correct end position
211 ScAddress& rScEnd = rScRange.aEnd;
212 if( !CheckAddress( rScEnd, bWarn ) )
214 rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) );
215 rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) );
216 rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) );
220 return bValidStart;
223 bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange,
224 const ScRange& rScRange, bool bWarn )
226 // check start position
227 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
228 if( bValidStart )
230 lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() );
232 // check & correct end position
233 SCCOL nScCol2 = rScRange.aEnd.Col();
234 SCROW nScRow2 = rScRange.aEnd.Row();
235 if( !CheckAddress( rScRange.aEnd, bWarn ) )
237 nScCol2 = ::std::min( nScCol2, maMaxPos.Col() );
238 nScRow2 = ::std::min( nScRow2, maMaxPos.Row() );
240 lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 );
242 return bValidStart;
245 // cell range list ------------------------------------------------------------
247 void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn )
249 for ( size_t nRange = rScRanges.size(); nRange > 0; )
251 ScRange* pScRange = rScRanges[ --nRange ];
252 if( !CheckRange( *pScRange, bWarn ) )
253 delete rScRanges.Remove(nRange);
257 void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges,
258 const ScRangeList& rScRanges, bool bWarn )
260 rXclRanges.clear();
261 for( size_t nPos = 0, nCount = rScRanges.size(); nPos < nCount; ++nPos )
263 if( const ScRange* pScRange = rScRanges[ nPos ] )
265 XclRange aXclRange( ScAddress::UNINITIALIZED );
266 if( ConvertRange( aXclRange, *pScRange, bWarn ) )
267 rXclRanges.push_back( aXclRange );
272 // EditEngine->String conversion ==============================================
274 namespace {
276 OUString lclGetUrlRepresentation( const SvxURLField& rUrlField )
278 const OUString& aRepr = rUrlField.GetRepresentation();
279 // no representation -> use URL
280 return aRepr.isEmpty() ? rUrlField.GetURL() : aRepr;
283 } // namespace
285 // ----------------------------------------------------------------------------
287 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
288 XclExpRoot( rRoot ),
289 maScPos( rScPos ),
290 mbMultipleUrls( false )
294 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
298 OUString XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField )
300 OUString aUrlRepr;
302 if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7
304 // there was/is already a HLINK record
305 mbMultipleUrls = static_cast< bool >(mxLinkRec);
307 mxLinkRec.reset( new XclExpHyperlink( GetRoot(), rUrlField, maScPos ) );
309 if( const String* pRepr = mxLinkRec->GetRepr() )
310 aUrlRepr = *pRepr;
312 // add URL to note text
313 maUrlList = ScGlobal::addToken( maUrlList, rUrlField.GetURL(), '\n' );
316 // no hyperlink representation from Excel HLINK record -> use it from text field
317 return aUrlRepr.isEmpty() ? lclGetUrlRepresentation(rUrlField) : aUrlRepr;
320 bool XclExpHyperlinkHelper::HasLinkRecord() const
322 return !mbMultipleUrls && mxLinkRec;
325 XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord()
327 if( HasLinkRecord() )
328 return mxLinkRec;
329 return XclExpHyperlinkRef();
332 // ----------------------------------------------------------------------------
334 namespace {
336 /** Creates a new formatted string from the passed unformatted string.
338 Creates a Unicode string or a byte string, depending on the current BIFF
339 version contained in the passed XclExpRoot object. May create a formatted
340 string object, if the text contains different script types.
342 @param pCellAttr
343 Cell attributes used for font formatting.
344 @param nFlags
345 Modifiers for string export.
346 @param nMaxLen
347 The maximum number of characters to store in this string.
348 @return
349 The new string object.
351 XclExpStringRef lclCreateFormattedString(
352 const XclExpRoot& rRoot, const String& rText, const ScPatternAttr* pCellAttr,
353 XclStrFlags nFlags, sal_uInt16 nMaxLen )
355 /* Create an empty Excel string object with correctly initialized BIFF mode,
356 because this function only uses Append() functions that require this. */
357 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
359 // script type handling
360 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
361 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
362 // #i63255# get script type for leading weak characters
363 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText );
365 // font buffer and cell item set
366 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
367 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
369 // process all script portions
370 OUString aOUText( rText );
371 sal_Int32 nPortionPos = 0;
372 sal_Int32 nTextLen = aOUText.getLength();
373 while( nPortionPos < nTextLen )
375 // get script type and end position of next script portion
376 sal_Int16 nScript = xBreakIt->getScriptType( aOUText, nPortionPos );
377 sal_Int32 nPortionEnd = xBreakIt->endOfScript( aOUText, nPortionPos, nScript );
379 // reuse previous script for following weak portions
380 if( nScript == ApiScriptType::WEAK )
381 nScript = nLastScript;
383 // construct font from current text portion
384 SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, rItemSet, nScript ) );
386 // Excel start position of this portion
387 sal_uInt16 nXclPortionStart = xString->Len();
388 // add portion text to Excel string
389 XclExpStringHelper::AppendString( *xString, rRoot, aOUText.copy( nPortionPos, nPortionEnd - nPortionPos ) );
390 if( nXclPortionStart < xString->Len() )
392 // insert font into buffer
393 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
394 // insert font index into format run vector
395 xString->AppendFormat( nXclPortionStart, nFontIdx );
398 // go to next script portion
399 nLastScript = nScript;
400 nPortionPos = nPortionEnd;
403 return xString;
406 /** Creates a new formatted string from an edit engine text object.
408 Creates a Unicode string or a byte string, depending on the current BIFF
409 version contained in the passed XclExpRoot object.
411 @param rEE
412 The edit engine in use. The text object must already be set.
413 @param nFlags
414 Modifiers for string export.
415 @param nMaxLen
416 The maximum number of characters to store in this string.
417 @return
418 The new string object.
420 XclExpStringRef lclCreateFormattedString(
421 const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper,
422 XclStrFlags nFlags, sal_uInt16 nMaxLen )
424 /* Create an empty Excel string object with correctly initialized BIFF mode,
425 because this function only uses Append() functions that require this. */
426 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
428 // font buffer and helper item set for edit engine -> Calc item conversion
429 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
430 SfxItemSet aItemSet( *rRoot.GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END );
432 // script type handling
433 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
434 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
435 // #i63255# get script type for leading weak characters
436 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() );
438 // process all paragraphs
439 sal_Int32 nParaCount = rEE.GetParagraphCount();
440 for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
442 ESelection aSel( nPara, 0 );
443 String aParaText( rEE.GetText( nPara ) );
445 std::vector<sal_uInt16> aPosList;
446 rEE.GetPortions( nPara, aPosList );
448 // process all portions in the paragraph
449 for( std::vector<sal_uInt16>::const_iterator it(aPosList.begin()); it != aPosList.end(); ++it )
451 aSel.nEndPos = static_cast< xub_StrLen >( *it );
452 String aXclPortionText( aParaText, aSel.nStartPos, aSel.nEndPos - aSel.nStartPos );
454 aItemSet.ClearItem();
455 SfxItemSet aEditSet( rEE.GetAttribs( aSel ) );
456 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
458 // get escapement value
459 short nEsc = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT ).GetEsc();
461 // process text fields
462 bool bIsHyperlink = false;
463 if( aSel.nStartPos + 1 == aSel.nEndPos )
465 // test if the character is a text field
466 const SfxPoolItem* pItem;
467 if( aEditSet.GetItemState( EE_FEATURE_FIELD, false, &pItem ) == SFX_ITEM_SET )
469 const SvxFieldData* pField = static_cast< const SvxFieldItem* >( pItem )->GetField();
470 if( const SvxURLField* pUrlField = PTR_CAST( SvxURLField, pField ) )
472 // convert URL field to string representation
473 aXclPortionText = pLinkHelper ?
474 pLinkHelper->ProcessUrlField( *pUrlField ) :
475 lclGetUrlRepresentation( *pUrlField );
476 bIsHyperlink = true;
478 else
480 OSL_FAIL( "lclCreateFormattedString - unknown text field" );
481 aXclPortionText.Erase();
486 // Excel start position of this portion
487 sal_uInt16 nXclPortionStart = xString->Len();
488 // add portion text to Excel string
489 XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText );
490 if( (nXclPortionStart < xString->Len()) || (aParaText.Len() == 0) )
492 /* Construct font from current edit engine text portion. Edit engine
493 creates different portions for different script types, no need to loop. */
494 sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 );
495 if( nScript == ApiScriptType::WEAK )
496 nScript = nLastScript;
497 SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, aItemSet, nScript ) );
498 nLastScript = nScript;
500 // add escapement
501 aFont.SetEscapement( nEsc );
502 // modify automatic font color for hyperlinks
503 if( bIsHyperlink && (GETITEM( aItemSet, SvxColorItem, ATTR_FONT_COLOR ).GetValue().GetColor() == COL_AUTO) )
504 aFont.SetColor( Color( COL_LIGHTBLUE ) );
506 // insert font into buffer
507 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
508 // insert font index into format run vector
509 xString->AppendFormat( nXclPortionStart, nFontIdx );
512 aSel.nStartPos = aSel.nEndPos;
515 // add trailing newline (important for correct character index calculation)
516 if( nPara + 1 < nParaCount )
517 XclExpStringHelper::AppendChar( *xString, rRoot, '\n' );
520 return xString;
523 } // namespace
525 // ----------------------------------------------------------------------------
527 XclExpStringRef XclExpStringHelper::CreateString(
528 const XclExpRoot& rRoot, const String& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
530 XclExpStringRef xString( new XclExpString );
531 if( rRoot.GetBiff() == EXC_BIFF8 )
532 xString->Assign( rString, nFlags, nMaxLen );
533 else
534 xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen );
535 return xString;
538 XclExpStringRef XclExpStringHelper::CreateString(
539 const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen )
541 XclExpStringRef xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
542 AppendChar( *xString, rRoot, cChar );
543 return xString;
546 void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, const String& rString )
548 if( rRoot.GetBiff() == EXC_BIFF8 )
549 rXclString.Append( rString );
550 else
551 rXclString.AppendByte( rString, rRoot.GetTextEncoding() );
554 void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar )
556 if( rRoot.GetBiff() == EXC_BIFF8 )
557 rXclString.Append( OUString(cChar) );
558 else
559 rXclString.AppendByte( cChar, rRoot.GetTextEncoding() );
562 XclExpStringRef XclExpStringHelper::CreateCellString(
563 const XclExpRoot& rRoot, const OUString& rString, const ScPatternAttr* pCellAttr,
564 XclStrFlags nFlags, sal_uInt16 nMaxLen )
566 return lclCreateFormattedString(rRoot, rString, pCellAttr, nFlags, nMaxLen);
569 XclExpStringRef XclExpStringHelper::CreateCellString(
570 const XclExpRoot& rRoot, const EditTextObject& rEditText, const ScPatternAttr* pCellAttr,
571 XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen )
573 XclExpStringRef xString;
575 // formatted cell
576 ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
577 sal_Bool bOldUpdateMode = rEE.GetUpdateMode();
578 rEE.SetUpdateMode( sal_True );
580 // default items
581 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
582 SfxItemSet* pEEItemSet = new SfxItemSet( rEE.GetEmptyItemSet() );
583 ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet );
584 rEE.SetDefaults( pEEItemSet ); // edit engine takes ownership
586 // create the string
587 rEE.SetText(rEditText);
588 xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen );
589 rEE.SetUpdateMode( bOldUpdateMode );
591 return xString;
594 XclExpStringRef XclExpStringHelper::CreateString(
595 const XclExpRoot& rRoot, const SdrTextObj& rTextObj,
596 XclStrFlags nFlags, sal_uInt16 nMaxLen )
598 XclExpStringRef xString;
599 if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() )
601 EditEngine& rEE = rRoot.GetDrawEditEngine();
602 sal_Bool bOldUpdateMode = rEE.GetUpdateMode();
603 rEE.SetUpdateMode( sal_True );
604 // create the string
605 rEE.SetText( pParaObj->GetTextObject() );
606 xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen );
607 rEE.SetUpdateMode( bOldUpdateMode );
608 // limit formats - TODO: BIFF dependent
609 if( !xString->IsEmpty() )
611 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
612 xString->AppendTrailingFormat( EXC_FONT_APP );
615 else
617 OSL_FAIL( "XclExpStringHelper::CreateString - textbox without para object" );
618 // create BIFF dependent empty Excel string
619 xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
621 return xString;
624 XclExpStringRef XclExpStringHelper::CreateString(
625 const XclExpRoot& rRoot, const EditTextObject& rEditObj,
626 XclStrFlags nFlags, sal_uInt16 nMaxLen )
628 XclExpStringRef xString;
629 EditEngine& rEE = rRoot.GetDrawEditEngine();
630 sal_Bool bOldUpdateMode = rEE.GetUpdateMode();
631 rEE.SetUpdateMode( sal_True );
632 rEE.SetText( rEditObj );
633 xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen );
634 rEE.SetUpdateMode( bOldUpdateMode );
635 // limit formats - TODO: BIFF dependent
636 if( !xString->IsEmpty() )
638 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
639 xString->AppendTrailingFormat( EXC_FONT_APP );
641 return xString;
644 sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const String& rString )
646 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
647 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
648 OUString aOUString( rString );
649 sal_Int32 nStrPos = 0;
650 sal_Int32 nStrLen = aOUString.getLength();
651 sal_Int16 nScript = ApiScriptType::WEAK;
652 while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) )
654 nScript = xBreakIt->getScriptType( aOUString, nStrPos );
655 nStrPos = xBreakIt->endOfScript( aOUString, nStrPos, nScript );
657 return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript;
660 // Header/footer conversion ===================================================
662 XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) :
663 XclExpRoot( rRoot ),
664 mrEE( rRoot.GetHFEditEngine() ),
665 mnTotalHeight( 0 )
669 void XclExpHFConverter::GenerateString(
670 const EditTextObject* pLeftObj,
671 const EditTextObject* pCenterObj,
672 const EditTextObject* pRightObj )
674 maHFString.Erase();
675 mnTotalHeight = 0;
676 AppendPortion( pLeftObj, 'L' );
677 AppendPortion( pCenterObj, 'C' );
678 AppendPortion( pRightObj, 'R' );
681 void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode )
683 if( !pTextObj ) return;
685 String aText;
686 sal_Int32 nHeight = 0;
687 SfxItemSet aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END );
689 // edit engine
690 sal_Bool bOldUpdateMode = mrEE.GetUpdateMode();
691 mrEE.SetUpdateMode( sal_True );
692 mrEE.SetText( *pTextObj );
694 // font information
695 XclFontData aFontData, aNewData;
696 if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
698 aFontData = pFirstFont->GetFontData();
699 (aFontData.mnHeight += 10) /= 20; // using pt here, not twips
701 else
702 aFontData.mnHeight = 10;
704 const FontList* pFontList = 0;
705 if( SfxObjectShell* pDocShell = GetDocShell() )
707 if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
708 pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
709 pFontList = pInfoItem->GetFontList();
712 sal_Int32 nParaCount = mrEE.GetParagraphCount();
713 for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
715 ESelection aSel( nPara, 0 );
716 String aParaText;
717 sal_Int32 nParaHeight = 0;
718 std::vector<sal_uInt16> aPosList;
719 mrEE.GetPortions( nPara, aPosList );
721 for( std::vector<sal_uInt16>::const_iterator it( aPosList.begin() ); it != aPosList.end(); ++it )
723 aSel.nEndPos = static_cast< xub_StrLen >( *it );
724 if( aSel.nStartPos < aSel.nEndPos )
727 // --- font attributes ---
729 Font aFont;
730 aItemSet.ClearItem();
731 SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) );
732 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
733 ScPatternAttr::GetFont( aFont, aItemSet, SC_AUTOCOL_RAW );
735 // font name and style
736 aNewData.maName = XclTools::GetXclFontName( aFont.GetName() );
737 aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL;
738 aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE);
739 bool bNewFont = !(aFontData.maName == aNewData.maName);
740 bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) ||
741 (aFontData.mbItalic != aNewData.mbItalic);
742 if( bNewFont || (bNewStyle && pFontList) )
744 aParaText.AppendAscii( "&\"" ).Append( aNewData.maName );
745 if( pFontList )
747 FontInfo aFontInfo( pFontList->Get(
748 aNewData.maName,
749 (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL,
750 aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) );
751 aNewData.maStyle = pFontList->GetStyleName( aFontInfo );
752 if( aNewData.maStyle.Len() )
753 aParaText.Append( ',' ).Append( aNewData.maStyle );
755 aParaText.Append( '"' );
758 // height
759 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
760 // -> get it directly from edit engine item set
761 aNewData.mnHeight = ulimit_cast< sal_uInt16 >( GETITEM( aEditSet, SvxFontHeightItem, EE_CHAR_FONTHEIGHT ).GetHeight() );
762 (aNewData.mnHeight += 10) /= 20;
763 bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight);
764 if( bFontHtChanged )
765 aParaText.Append( '&' ).Append( OUString::number( aNewData.mnHeight ) );
766 // update maximum paragraph height, convert to twips
767 nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 );
769 // underline
770 aNewData.mnUnderline = EXC_FONTUNDERL_NONE;
771 switch( aFont.GetUnderline() )
773 case UNDERLINE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break;
774 case UNDERLINE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break;
775 case UNDERLINE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
776 default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE;
778 if( aFontData.mnUnderline != aNewData.mnUnderline )
780 sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ?
781 aFontData.mnUnderline : aNewData.mnUnderline;
782 aParaText.AppendAscii( (nTmpUnderl == EXC_FONTUNDERL_SINGLE) ? "&U" : "&E" );
785 // strikeout
786 aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE);
787 if( aFontData.mbStrikeout != aNewData.mbStrikeout )
788 aParaText.AppendAscii( "&S" );
790 // super/sub script
791 const SvxEscapementItem& rEscapeItem = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT );
792 aNewData.SetScEscapement( rEscapeItem.GetEsc() );
793 if( aFontData.mnEscapem != aNewData.mnEscapem )
795 switch(aNewData.mnEscapem)
797 // close the previous super/sub script.
798 case EXC_FONTESC_NONE: aParaText.AppendAscii( (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? "&X" : "&Y" ); break;
799 case EXC_FONTESC_SUPER: aParaText.AppendAscii( "&X" ); break;
800 case EXC_FONTESC_SUB: aParaText.AppendAscii( "&Y" ); break;
801 default: break;
805 aFontData = aNewData;
807 // --- text content or text fields ---
809 const SfxPoolItem* pItem;
810 if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters
811 (aEditSet.GetItemState( EE_FEATURE_FIELD, false, &pItem ) == SFX_ITEM_SET) )
813 if( const SvxFieldData* pFieldData = static_cast< const SvxFieldItem* >( pItem )->GetField() )
815 if( pFieldData->ISA( SvxPageField ) )
816 aParaText.AppendAscii( "&P" );
817 else if( pFieldData->ISA( SvxPagesField ) )
818 aParaText.AppendAscii( "&N" );
819 else if( pFieldData->ISA( SvxDateField ) )
820 aParaText.AppendAscii( "&D" );
821 else if( pFieldData->ISA( SvxTimeField ) || pFieldData->ISA( SvxExtTimeField ) )
822 aParaText.AppendAscii( "&T" );
823 else if( pFieldData->ISA( SvxTableField ) )
824 aParaText.AppendAscii( "&A" );
825 else if( pFieldData->ISA( SvxFileField ) ) // title -> file name
826 aParaText.AppendAscii( "&F" );
827 else if( const SvxExtFileField* pFileField = PTR_CAST( SvxExtFileField, pFieldData ) )
829 switch( pFileField->GetFormat() )
831 case SVXFILEFORMAT_NAME_EXT:
832 case SVXFILEFORMAT_NAME:
833 aParaText.AppendAscii( "&F" );
834 break;
835 case SVXFILEFORMAT_PATH:
836 aParaText.AppendAscii( "&Z" );
837 break;
838 case SVXFILEFORMAT_FULLPATH:
839 aParaText.AppendAscii( "&Z&F" );
840 break;
841 default:
842 OSL_FAIL( "XclExpHFConverter::AppendPortion - unknown file field" );
847 else
849 String aPortionText( mrEE.GetText( aSel ) );
850 aPortionText.SearchAndReplaceAll( OUString('&'), OUString("&&") );
851 // #i17440# space between font height and numbers in text
852 if( bFontHtChanged && aParaText.Len() && aPortionText.Len() )
854 sal_Unicode cLast = aParaText.GetChar( aParaText.Len() - 1 );
855 sal_Unicode cFirst = aPortionText.GetChar( 0 );
856 if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') )
857 aParaText.Append( ' ' );
859 aParaText.Append( aPortionText );
863 aSel.nStartPos = aSel.nEndPos;
866 aText = ScGlobal::addToken( aText, aParaText, '\n' );
867 if( nParaHeight == 0 )
868 nParaHeight = aFontData.mnHeight * 20; // points -> twips
869 nHeight += nParaHeight;
872 mrEE.SetUpdateMode( bOldUpdateMode );
874 if( aText.Len() )
876 maHFString.Append( '&' ).Append( cPortionCode ).Append( aText );
877 mnTotalHeight = ::std::max( mnTotalHeight, nHeight );
881 // URL conversion =============================================================
883 namespace {
885 /** Encodes special parts of the URL, i.e. directory separators and volume names.
886 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
887 OUString lclEncodeDosUrl(
888 XclBiff eBiff, const OUString& rUrl, const OUString& rBase, const OUString* pTableName)
890 OUStringBuffer aBuf;
892 if (!rUrl.isEmpty())
894 OUString aOldUrl = rUrl;
895 aBuf.append(EXC_URLSTART_ENCODED);
897 if ( aOldUrl.getLength() > 2 && aOldUrl.copy(0,2) == "\\\\" )
899 // UNC
900 aBuf.append(EXC_URL_DOSDRIVE).append(sal_Unicode('@'));
901 aOldUrl = aOldUrl.copy(2);
903 else if ( aOldUrl.getLength() > 2 && aOldUrl.copy(1,2) == ":\\" )
905 // drive letter
906 sal_Unicode cThisDrive = rBase.isEmpty() ? ' ' : rBase.getStr()[0];
907 sal_Unicode cDrive = aOldUrl.getStr()[0];
908 if (cThisDrive == cDrive)
909 // This document and the referenced document are under the same drive.
910 aBuf.append(EXC_URL_DRIVEROOT);
911 else
912 aBuf.append(EXC_URL_DOSDRIVE).append(cDrive);
913 aOldUrl = aOldUrl.copy(3);
916 // directories
917 sal_Int32 nPos = -1;
918 while((nPos = aOldUrl.indexOf('\\')) != -1)
920 if ( aOldUrl.copy(0,2) == ".." )
921 // parent dir (NOTE: the MS-XLS spec doesn't mention this, and
922 // Excel seems confused by this token).
923 aBuf.append(EXC_URL_PARENTDIR);
924 else
925 aBuf.append(aOldUrl.copy(0,nPos)).append(EXC_URL_SUBDIR);
927 aOldUrl = aOldUrl.copy(nPos + 1);
930 // file name
931 if (pTableName) // enclose file name in brackets if table name follows
932 aBuf.append(sal_Unicode('[')).append(aOldUrl).append(sal_Unicode(']'));
933 else
934 aBuf.append(aOldUrl);
936 else // empty URL -> self reference
938 switch( eBiff )
940 case EXC_BIFF5:
941 aBuf.append(pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF);
942 break;
943 case EXC_BIFF8:
944 DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" );
945 aBuf.append(EXC_URLSTART_SELF);
946 break;
947 default:
948 DBG_ERROR_BIFF();
952 // table name
953 if (pTableName)
954 aBuf.append(*pTableName);
956 return aBuf.makeStringAndClear();
959 } // namespace
961 // ----------------------------------------------------------------------------
963 OUString XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, const OUString& rAbsUrl, const OUString* pTableName )
965 OUString aDosUrl = INetURLObject(rAbsUrl).getFSysPath(INetURLObject::FSYS_DOS);
966 OUString aDosBase = INetURLObject(rRoot.GetBasePath()).getFSysPath(INetURLObject::FSYS_DOS);
967 return lclEncodeDosUrl(rRoot.GetBiff(), aDosUrl, aDosBase, pTableName);
970 OUString XclExpUrlHelper::EncodeDde( const OUString& rApplic, const OUString& rTopic )
972 OUStringBuffer aBuf;
973 aBuf.append(rApplic).append(EXC_DDE_DELIM).append(rTopic);
974 return aBuf.makeStringAndClear();
977 // Cached Value Lists =========================================================
979 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix )
980 : mrMatrix( rMatrix )
982 mrMatrix.IncRef();
984 XclExpCachedMatrix::~XclExpCachedMatrix()
986 mrMatrix.DecRef();
989 void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const
991 mrMatrix.GetDimensions( nCols, nRows );
993 OSL_ENSURE( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" );
994 OSL_ENSURE( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
997 sal_Size XclExpCachedMatrix::GetSize() const
999 SCSIZE nCols, nRows;
1001 GetDimensions( nCols, nRows );
1003 /* The returned size may be wrong if the matrix contains strings. The only
1004 effect is that the export stream has to update a wrong record size which is
1005 faster than to iterate through all cached values and calculate their sizes. */
1006 return 3 + 9 * (nCols * nRows);
1009 void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const
1011 SCSIZE nCols, nRows;
1013 GetDimensions( nCols, nRows );
1015 if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
1016 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1017 rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows );
1018 else
1019 // in BIFF8: columns and rows decreaed by 1
1020 rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 );
1022 for( SCSIZE nRow = 0; nRow < nRows; ++nRow )
1024 for( SCSIZE nCol = 0; nCol < nCols; ++nCol )
1026 ScMatrixValue nMatVal = mrMatrix.Get( nCol, nRow );
1028 if( SC_MATVAL_EMPTY == nMatVal.nType )
1030 rStrm.SetSliceSize( 9 );
1031 rStrm << EXC_CACHEDVAL_EMPTY;
1032 rStrm.WriteZeroBytes( 8 );
1034 else if( ScMatrix::IsNonValueType( nMatVal.nType ) )
1036 XclExpString aStr( nMatVal.GetString(), EXC_STR_DEFAULT );
1037 rStrm.SetSliceSize( 6 );
1038 rStrm << EXC_CACHEDVAL_STRING << aStr;
1040 else if( SC_MATVAL_BOOLEAN == nMatVal.nType )
1042 sal_Int8 nBool = nMatVal.GetBoolean();
1043 rStrm.SetSliceSize( 9 );
1044 rStrm << EXC_CACHEDVAL_BOOL << nBool;
1045 rStrm.WriteZeroBytes( 7 );
1047 else if( sal_uInt16 nScError = nMatVal.GetError() )
1049 sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) );
1050 rStrm.SetSliceSize( 9 );
1051 rStrm << EXC_CACHEDVAL_ERROR << nError;
1052 rStrm.WriteZeroBytes( 7 );
1054 else
1056 rStrm.SetSliceSize( 9 );
1057 rStrm << EXC_CACHEDVAL_DOUBLE << nMatVal.fVal;
1063 // ============================================================================
1065 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */