Version 4.0.2.1, tag libreoffice-4.0.2.1
[LibreOffice.git] / sc / source / filter / excel / xehelper.cxx
blobc493560432d9321c0be2c0ea43d723411b94acae
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/escpitem.hxx>
36 #include <editeng/svxfont.hxx>
38 #include "document.hxx"
39 #include "docpool.hxx"
40 #include "cell.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 ::rtl::OUString;
51 using ::com::sun::star::uno::Reference;
52 using ::com::sun::star::i18n::XBreakIterator;
54 // Export progress bar ========================================================
56 XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) :
57 XclExpRoot( rRoot ),
58 mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ),
59 mpSubProgress( 0 ),
60 mpSubRowCreate( 0 ),
61 mpSubRowFinal( 0 ),
62 mnSegRowFinal( SCF_INV_SEGMENT ),
63 mnRowCount( 0 )
67 XclExpProgressBar::~XclExpProgressBar()
71 void XclExpProgressBar::Initialize()
73 const ScDocument& rDoc = GetDoc();
74 const XclExpTabInfo& rTabInfo = GetTabInfo();
75 SCTAB nScTabCount = rTabInfo.GetScTabCount();
77 // *** segment: creation of ROW records *** -------------------------------
79 sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 );
80 mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate );
81 maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT );
83 for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab )
85 if( rTabInfo.IsExportTab( nScTab ) )
87 SCCOL nLastUsedScCol;
88 SCROW nLastUsedScRow;
89 rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
90 sal_Size nSegSize = static_cast< sal_Size >( nLastUsedScRow + 1 );
91 maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize );
95 // *** segment: writing all ROW records *** -------------------------------
97 mnSegRowFinal = mxProgress->AddSegment( 1000 );
98 // sub progress bar and segment are created later in ActivateFinalRowsSegment()
101 void XclExpProgressBar::IncRowRecordCount()
103 ++mnRowCount;
106 void XclExpProgressBar::ActivateCreateRowsSegment()
108 OSL_ENSURE( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
109 "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
110 sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ];
111 OSL_ENSURE( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
112 if( nSeg != SCF_INV_SEGMENT )
114 mpSubProgress = mpSubRowCreate;
115 mpSubProgress->ActivateSegment( nSeg );
117 else
118 mpSubProgress = 0;
121 void XclExpProgressBar::ActivateFinalRowsSegment()
123 if( !mpSubRowFinal && (mnRowCount > 0) )
125 mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal );
126 mpSubRowFinal->AddSegment( mnRowCount );
128 mpSubProgress = mpSubRowFinal;
129 if( mpSubProgress )
130 mpSubProgress->Activate();
133 void XclExpProgressBar::Progress()
135 if( mpSubProgress && !mpSubProgress->IsFull() )
136 mpSubProgress->Progress();
139 // Calc->Excel cell address/range conversion ==================================
141 namespace {
143 /** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
144 inline void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow )
146 rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol );
147 rXclPos.mnRow = static_cast< sal_uInt32 >( nScRow );
150 } // namespace
152 // ----------------------------------------------------------------------------
154 XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) :
155 XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() )
159 // cell address ---------------------------------------------------------------
161 bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn )
163 // ScAddress::operator<=() doesn't do what we want here
164 bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col());
165 bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row());
166 bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab());
168 bool bValid = bValidCol && bValidRow && bValidTab;
169 if( !bValid && bWarn )
171 mbColTrunc |= !bValidCol;
172 mbRowTrunc |= !bValidRow;
173 mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs
174 mrTracer.TraceInvalidAddress( rScPos, maMaxPos );
176 return bValid;
179 bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos,
180 const ScAddress& rScPos, bool bWarn )
182 bool bValid = CheckAddress( rScPos, bWarn );
183 if( bValid )
184 lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() );
185 return bValid;
188 XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn )
190 XclAddress aXclPos( ScAddress::UNINITIALIZED );
191 if( !ConvertAddress( aXclPos, rScPos, bWarn ) )
192 lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) );
193 return aXclPos;
196 // cell range -----------------------------------------------------------------
198 bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn )
200 return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn );
203 bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn )
205 rScRange.Justify();
207 // check start position
208 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
209 if( bValidStart )
211 // check & correct end position
212 ScAddress& rScEnd = rScRange.aEnd;
213 if( !CheckAddress( rScEnd, bWarn ) )
215 rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) );
216 rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) );
217 rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) );
221 return bValidStart;
224 bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange,
225 const ScRange& rScRange, bool bWarn )
227 // check start position
228 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
229 if( bValidStart )
231 lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() );
233 // check & correct end position
234 SCCOL nScCol2 = rScRange.aEnd.Col();
235 SCROW nScRow2 = rScRange.aEnd.Row();
236 if( !CheckAddress( rScRange.aEnd, bWarn ) )
238 nScCol2 = ::std::min( nScCol2, maMaxPos.Col() );
239 nScRow2 = ::std::min( nScRow2, maMaxPos.Row() );
241 lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 );
243 return bValidStart;
246 // cell range list ------------------------------------------------------------
248 void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn )
250 for ( size_t nRange = rScRanges.size(); nRange > 0; )
252 ScRange* pScRange = rScRanges[ --nRange ];
253 if( !CheckRange( *pScRange, bWarn ) )
254 delete rScRanges.Remove(nRange);
258 void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges,
259 const ScRangeList& rScRanges, bool bWarn )
261 rXclRanges.clear();
262 for( size_t nPos = 0, nCount = rScRanges.size(); nPos < nCount; ++nPos )
264 if( const ScRange* pScRange = rScRanges[ nPos ] )
266 XclRange aXclRange( ScAddress::UNINITIALIZED );
267 if( ConvertRange( aXclRange, *pScRange, bWarn ) )
268 rXclRanges.push_back( aXclRange );
273 // EditEngine->String conversion ==============================================
275 namespace {
277 rtl::OUString lclGetUrlRepresentation( const SvxURLField& rUrlField )
279 const rtl::OUString& aRepr = rUrlField.GetRepresentation();
280 // no representation -> use URL
281 return aRepr.isEmpty() ? rUrlField.GetURL() : aRepr;
284 } // namespace
286 // ----------------------------------------------------------------------------
288 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
289 XclExpRoot( rRoot ),
290 maScPos( rScPos ),
291 mbMultipleUrls( false )
295 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
299 rtl::OUString XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField )
301 rtl::OUString aUrlRepr;
303 if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7
305 // there was/is already a HLINK record
306 mbMultipleUrls = mxLinkRec;
308 mxLinkRec.reset( new XclExpHyperlink( GetRoot(), rUrlField, maScPos ) );
310 if( const String* pRepr = mxLinkRec->GetRepr() )
311 aUrlRepr = *pRepr;
313 // add URL to note text
314 maUrlList = ScGlobal::addToken( maUrlList, rUrlField.GetURL(), '\n' );
317 // no hyperlink representation from Excel HLINK record -> use it from text field
318 return aUrlRepr.isEmpty() ? lclGetUrlRepresentation(rUrlField) : aUrlRepr;
321 bool XclExpHyperlinkHelper::HasLinkRecord() const
323 return !mbMultipleUrls && mxLinkRec;
326 XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord()
328 if( HasLinkRecord() )
329 return mxLinkRec;
330 return XclExpHyperlinkRef();
333 // ----------------------------------------------------------------------------
335 namespace {
337 /** Creates a new formatted string from the passed unformatted string.
339 Creates a Unicode string or a byte string, depending on the current BIFF
340 version contained in the passed XclExpRoot object. May create a formatted
341 string object, if the text contains different script types.
343 @param pCellAttr
344 Cell attributes used for font formatting.
345 @param nFlags
346 Modifiers for string export.
347 @param nMaxLen
348 The maximum number of characters to store in this string.
349 @return
350 The new string object.
352 XclExpStringRef lclCreateFormattedString(
353 const XclExpRoot& rRoot, const String& rText, const ScPatternAttr* pCellAttr,
354 XclStrFlags nFlags, sal_uInt16 nMaxLen )
356 /* Create an empty Excel string object with correctly initialized BIFF mode,
357 because this function only uses Append() functions that require this. */
358 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
360 // script type handling
361 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
362 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
363 // #i63255# get script type for leading weak characters
364 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText );
366 // font buffer and cell item set
367 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
368 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
370 // process all script portions
371 OUString aOUText( rText );
372 sal_Int32 nPortionPos = 0;
373 sal_Int32 nTextLen = aOUText.getLength();
374 while( nPortionPos < nTextLen )
376 // get script type and end position of next script portion
377 sal_Int16 nScript = xBreakIt->getScriptType( aOUText, nPortionPos );
378 sal_Int32 nPortionEnd = xBreakIt->endOfScript( aOUText, nPortionPos, nScript );
380 // reuse previous script for following weak portions
381 if( nScript == ApiScriptType::WEAK )
382 nScript = nLastScript;
384 // construct font from current text portion
385 SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, rItemSet, nScript ) );
387 // Excel start position of this portion
388 sal_uInt16 nXclPortionStart = xString->Len();
389 // add portion text to Excel string
390 XclExpStringHelper::AppendString( *xString, rRoot, aOUText.copy( nPortionPos, nPortionEnd - nPortionPos ) );
391 if( nXclPortionStart < xString->Len() )
393 // insert font into buffer
394 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
395 // insert font index into format run vector
396 xString->AppendFormat( nXclPortionStart, nFontIdx );
399 // go to next script portion
400 nLastScript = nScript;
401 nPortionPos = nPortionEnd;
404 return xString;
407 /** Creates a new formatted string from an edit engine text object.
409 Creates a Unicode string or a byte string, depending on the current BIFF
410 version contained in the passed XclExpRoot object.
412 @param rEE
413 The edit engine in use. The text object must already be set.
414 @param nFlags
415 Modifiers for string export.
416 @param nMaxLen
417 The maximum number of characters to store in this string.
418 @return
419 The new string object.
421 XclExpStringRef lclCreateFormattedString(
422 const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper,
423 XclStrFlags nFlags, sal_uInt16 nMaxLen )
425 /* Create an empty Excel string object with correctly initialized BIFF mode,
426 because this function only uses Append() functions that require this. */
427 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
429 // font buffer and helper item set for edit engine -> Calc item conversion
430 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
431 SfxItemSet aItemSet( *rRoot.GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END );
433 // script type handling
434 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
435 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
436 // #i63255# get script type for leading weak characters
437 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() );
439 // process all paragraphs
440 sal_uInt16 nParaCount = rEE.GetParagraphCount();
441 for( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara )
443 ESelection aSel( nPara, 0 );
444 String aParaText( rEE.GetText( nPara ) );
446 std::vector<sal_uInt16> aPosList;
447 rEE.GetPortions( nPara, aPosList );
449 // process all portions in the paragraph
450 for( std::vector<sal_uInt16>::const_iterator it(aPosList.begin()); it != aPosList.end(); ++it )
452 aSel.nEndPos = static_cast< xub_StrLen >( *it );
453 String aXclPortionText( aParaText, aSel.nStartPos, aSel.nEndPos - aSel.nStartPos );
455 aItemSet.ClearItem();
456 SfxItemSet aEditSet( rEE.GetAttribs( aSel ) );
457 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
459 // get escapement value
460 short nEsc = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT ).GetEsc();
462 // process text fields
463 bool bIsHyperlink = false;
464 if( aSel.nStartPos + 1 == aSel.nEndPos )
466 // test if the character is a text field
467 const SfxPoolItem* pItem;
468 if( aEditSet.GetItemState( EE_FEATURE_FIELD, false, &pItem ) == SFX_ITEM_SET )
470 const SvxFieldData* pField = static_cast< const SvxFieldItem* >( pItem )->GetField();
471 if( const SvxURLField* pUrlField = PTR_CAST( SvxURLField, pField ) )
473 // convert URL field to string representation
474 aXclPortionText = pLinkHelper ?
475 pLinkHelper->ProcessUrlField( *pUrlField ) :
476 lclGetUrlRepresentation( *pUrlField );
477 bIsHyperlink = true;
479 else
481 OSL_FAIL( "lclCreateFormattedString - unknown text field" );
482 aXclPortionText.Erase();
487 // Excel start position of this portion
488 sal_uInt16 nXclPortionStart = xString->Len();
489 // add portion text to Excel string
490 XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText );
491 if( (nXclPortionStart < xString->Len()) || (aParaText.Len() == 0) )
493 /* Construct font from current edit engine text portion. Edit engine
494 creates different portions for different script types, no need to loop. */
495 sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 );
496 if( nScript == ApiScriptType::WEAK )
497 nScript = nLastScript;
498 SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, aItemSet, nScript ) );
499 nLastScript = nScript;
501 // add escapement
502 aFont.SetEscapement( nEsc );
503 // modify automatic font color for hyperlinks
504 if( bIsHyperlink && (GETITEM( aItemSet, SvxColorItem, ATTR_FONT_COLOR ).GetValue().GetColor() == COL_AUTO) )
505 aFont.SetColor( Color( COL_LIGHTBLUE ) );
507 // insert font into buffer
508 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
509 // insert font index into format run vector
510 xString->AppendFormat( nXclPortionStart, nFontIdx );
513 aSel.nStartPos = aSel.nEndPos;
516 // add trailing newline (important for correct character index calculation)
517 if( nPara + 1 < nParaCount )
518 XclExpStringHelper::AppendChar( *xString, rRoot, '\n' );
521 return xString;
524 } // namespace
526 // ----------------------------------------------------------------------------
528 XclExpStringRef XclExpStringHelper::CreateString(
529 const XclExpRoot& rRoot, const String& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
531 XclExpStringRef xString( new XclExpString );
532 if( rRoot.GetBiff() == EXC_BIFF8 )
533 xString->Assign( rString, nFlags, nMaxLen );
534 else
535 xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen );
536 return xString;
539 XclExpStringRef XclExpStringHelper::CreateString(
540 const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen )
542 XclExpStringRef xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
543 AppendChar( *xString, rRoot, cChar );
544 return xString;
547 void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, const String& rString )
549 if( rRoot.GetBiff() == EXC_BIFF8 )
550 rXclString.Append( rString );
551 else
552 rXclString.AppendByte( rString, rRoot.GetTextEncoding() );
555 void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar )
557 if( rRoot.GetBiff() == EXC_BIFF8 )
558 rXclString.Append( rtl::OUString(cChar) );
559 else
560 rXclString.AppendByte( cChar, rRoot.GetTextEncoding() );
563 XclExpStringRef XclExpStringHelper::CreateCellString(
564 const XclExpRoot& rRoot, const ScStringCell& rStringCell, const ScPatternAttr* pCellAttr,
565 XclStrFlags nFlags, sal_uInt16 nMaxLen )
567 rtl::OUString aCellText = rStringCell.GetString();
568 return lclCreateFormattedString( rRoot, aCellText, pCellAttr, nFlags, nMaxLen );
571 XclExpStringRef XclExpStringHelper::CreateCellString(
572 const XclExpRoot& rRoot, const ScEditCell& rEditCell, const ScPatternAttr* pCellAttr,
573 XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen )
575 XclExpStringRef xString;
576 if( const EditTextObject* pEditObj = rEditCell.GetData() )
578 // formatted cell
579 ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
580 sal_Bool bOldUpdateMode = rEE.GetUpdateMode();
581 rEE.SetUpdateMode( sal_True );
582 // default items
583 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
584 SfxItemSet* pEEItemSet = new SfxItemSet( rEE.GetEmptyItemSet() );
585 ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet );
586 rEE.SetDefaults( pEEItemSet ); // edit engine takes ownership
587 // create the string
588 rEE.SetText( *pEditObj );
589 xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen );
590 rEE.SetUpdateMode( bOldUpdateMode );
592 else
594 // unformatted cell
595 String aCellText = rEditCell.GetString();
596 xString = lclCreateFormattedString( rRoot, aCellText, pCellAttr, nFlags, nMaxLen );
598 return xString;
601 XclExpStringRef XclExpStringHelper::CreateString(
602 const XclExpRoot& rRoot, const SdrTextObj& rTextObj,
603 XclStrFlags nFlags, sal_uInt16 nMaxLen )
605 XclExpStringRef xString;
606 if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() )
608 EditEngine& rEE = rRoot.GetDrawEditEngine();
609 sal_Bool bOldUpdateMode = rEE.GetUpdateMode();
610 rEE.SetUpdateMode( sal_True );
611 // create the string
612 rEE.SetText( pParaObj->GetTextObject() );
613 xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen );
614 rEE.SetUpdateMode( bOldUpdateMode );
615 // limit formats - TODO: BIFF dependent
616 if( !xString->IsEmpty() )
618 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
619 xString->AppendTrailingFormat( EXC_FONT_APP );
622 else
624 OSL_FAIL( "XclExpStringHelper::CreateString - textbox without para object" );
625 // create BIFF dependent empty Excel string
626 xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
628 return xString;
631 XclExpStringRef XclExpStringHelper::CreateString(
632 const XclExpRoot& rRoot, const EditTextObject& rEditObj,
633 XclStrFlags nFlags, sal_uInt16 nMaxLen )
635 XclExpStringRef xString;
636 EditEngine& rEE = rRoot.GetDrawEditEngine();
637 sal_Bool bOldUpdateMode = rEE.GetUpdateMode();
638 rEE.SetUpdateMode( sal_True );
639 rEE.SetText( rEditObj );
640 xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen );
641 rEE.SetUpdateMode( bOldUpdateMode );
642 // limit formats - TODO: BIFF dependent
643 if( !xString->IsEmpty() )
645 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
646 xString->AppendTrailingFormat( EXC_FONT_APP );
648 return xString;
651 sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const String& rString )
653 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
654 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
655 OUString aOUString( rString );
656 sal_Int32 nStrPos = 0;
657 sal_Int32 nStrLen = aOUString.getLength();
658 sal_Int16 nScript = ApiScriptType::WEAK;
659 while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) )
661 nScript = xBreakIt->getScriptType( aOUString, nStrPos );
662 nStrPos = xBreakIt->endOfScript( aOUString, nStrPos, nScript );
664 return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript;
667 // Header/footer conversion ===================================================
669 XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) :
670 XclExpRoot( rRoot ),
671 mrEE( rRoot.GetHFEditEngine() ),
672 mnTotalHeight( 0 )
676 void XclExpHFConverter::GenerateString(
677 const EditTextObject* pLeftObj,
678 const EditTextObject* pCenterObj,
679 const EditTextObject* pRightObj )
681 maHFString.Erase();
682 mnTotalHeight = 0;
683 AppendPortion( pLeftObj, 'L' );
684 AppendPortion( pCenterObj, 'C' );
685 AppendPortion( pRightObj, 'R' );
688 void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode )
690 if( !pTextObj ) return;
692 String aText;
693 sal_Int32 nHeight = 0;
694 SfxItemSet aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END );
696 // edit engine
697 sal_Bool bOldUpdateMode = mrEE.GetUpdateMode();
698 mrEE.SetUpdateMode( sal_True );
699 mrEE.SetText( *pTextObj );
701 // font information
702 XclFontData aFontData, aNewData;
703 if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
705 aFontData = pFirstFont->GetFontData();
706 (aFontData.mnHeight += 10) /= 20; // using pt here, not twips
708 else
709 aFontData.mnHeight = 10;
711 const FontList* pFontList = 0;
712 if( SfxObjectShell* pDocShell = GetDocShell() )
714 if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
715 pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
716 pFontList = pInfoItem->GetFontList();
719 sal_uInt16 nParaCount = mrEE.GetParagraphCount();
720 for( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara )
722 ESelection aSel( nPara, 0 );
723 String aParaText;
724 sal_Int32 nParaHeight = 0;
725 std::vector<sal_uInt16> aPosList;
726 mrEE.GetPortions( nPara, aPosList );
728 for( std::vector<sal_uInt16>::const_iterator it( aPosList.begin() ); it != aPosList.end(); ++it )
730 aSel.nEndPos = static_cast< xub_StrLen >( *it );
731 if( aSel.nStartPos < aSel.nEndPos )
734 // --- font attributes ---
736 Font aFont;
737 aItemSet.ClearItem();
738 SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) );
739 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
740 ScPatternAttr::GetFont( aFont, aItemSet, SC_AUTOCOL_RAW );
742 // font name and style
743 aNewData.maName = XclTools::GetXclFontName( aFont.GetName() );
744 aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL;
745 aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE);
746 bool bNewFont = !(aFontData.maName == aNewData.maName);
747 bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) ||
748 (aFontData.mbItalic != aNewData.mbItalic);
749 if( bNewFont || (bNewStyle && pFontList) )
751 aParaText.AppendAscii( "&\"" ).Append( aNewData.maName );
752 if( pFontList )
754 FontInfo aFontInfo( pFontList->Get(
755 aNewData.maName,
756 (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL,
757 aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) );
758 aNewData.maStyle = pFontList->GetStyleName( aFontInfo );
759 if( aNewData.maStyle.Len() )
760 aParaText.Append( ',' ).Append( aNewData.maStyle );
762 aParaText.Append( '"' );
765 // height
766 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
767 // -> get it directly from edit engine item set
768 aNewData.mnHeight = ulimit_cast< sal_uInt16 >( GETITEM( aEditSet, SvxFontHeightItem, EE_CHAR_FONTHEIGHT ).GetHeight() );
769 (aNewData.mnHeight += 10) /= 20;
770 bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight);
771 if( bFontHtChanged )
772 aParaText.Append( '&' ).Append( String::CreateFromInt32( aNewData.mnHeight ) );
773 // update maximum paragraph height, convert to twips
774 nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 );
776 // underline
777 aNewData.mnUnderline = EXC_FONTUNDERL_NONE;
778 switch( aFont.GetUnderline() )
780 case UNDERLINE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break;
781 case UNDERLINE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break;
782 case UNDERLINE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
783 default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE;
785 if( aFontData.mnUnderline != aNewData.mnUnderline )
787 sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ?
788 aFontData.mnUnderline : aNewData.mnUnderline;
789 aParaText.AppendAscii( (nTmpUnderl == EXC_FONTUNDERL_SINGLE) ? "&U" : "&E" );
792 // strikeout
793 aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE);
794 if( aFontData.mbStrikeout != aNewData.mbStrikeout )
795 aParaText.AppendAscii( "&S" );
797 // super/sub script
798 const SvxEscapementItem& rEscapeItem = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT );
799 aNewData.SetScEscapement( rEscapeItem.GetEsc() );
800 if( aFontData.mnEscapem != aNewData.mnEscapem )
802 switch(aNewData.mnEscapem)
804 // close the previous super/sub script.
805 case EXC_FONTESC_NONE: aParaText.AppendAscii( (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? "&X" : "&Y" ); break;
806 case EXC_FONTESC_SUPER: aParaText.AppendAscii( "&X" ); break;
807 case EXC_FONTESC_SUB: aParaText.AppendAscii( "&Y" ); break;
808 default: break;
812 aFontData = aNewData;
814 // --- text content or text fields ---
816 const SfxPoolItem* pItem;
817 if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters
818 (aEditSet.GetItemState( EE_FEATURE_FIELD, false, &pItem ) == SFX_ITEM_SET) )
820 if( const SvxFieldData* pFieldData = static_cast< const SvxFieldItem* >( pItem )->GetField() )
822 if( pFieldData->ISA( SvxPageField ) )
823 aParaText.AppendAscii( "&P" );
824 else if( pFieldData->ISA( SvxPagesField ) )
825 aParaText.AppendAscii( "&N" );
826 else if( pFieldData->ISA( SvxDateField ) )
827 aParaText.AppendAscii( "&D" );
828 else if( pFieldData->ISA( SvxTimeField ) || pFieldData->ISA( SvxExtTimeField ) )
829 aParaText.AppendAscii( "&T" );
830 else if( pFieldData->ISA( SvxTableField ) )
831 aParaText.AppendAscii( "&A" );
832 else if( pFieldData->ISA( SvxFileField ) ) // title -> file name
833 aParaText.AppendAscii( "&F" );
834 else if( const SvxExtFileField* pFileField = PTR_CAST( SvxExtFileField, pFieldData ) )
836 switch( pFileField->GetFormat() )
838 case SVXFILEFORMAT_NAME_EXT:
839 case SVXFILEFORMAT_NAME:
840 aParaText.AppendAscii( "&F" );
841 break;
842 case SVXFILEFORMAT_PATH:
843 aParaText.AppendAscii( "&Z" );
844 break;
845 case SVXFILEFORMAT_FULLPATH:
846 aParaText.AppendAscii( "&Z&F" );
847 break;
848 default:
849 OSL_FAIL( "XclExpHFConverter::AppendPortion - unknown file field" );
854 else
856 String aPortionText( mrEE.GetText( aSel ) );
857 aPortionText.SearchAndReplaceAll( rtl::OUString('&'), rtl::OUString("&&") );
858 // #i17440# space between font height and numbers in text
859 if( bFontHtChanged && aParaText.Len() && aPortionText.Len() )
861 sal_Unicode cLast = aParaText.GetChar( aParaText.Len() - 1 );
862 sal_Unicode cFirst = aPortionText.GetChar( 0 );
863 if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') )
864 aParaText.Append( ' ' );
866 aParaText.Append( aPortionText );
870 aSel.nStartPos = aSel.nEndPos;
873 aText = ScGlobal::addToken( aText, aParaText, '\n' );
874 if( nParaHeight == 0 )
875 nParaHeight = aFontData.mnHeight * 20; // points -> twips
876 nHeight += nParaHeight;
879 mrEE.SetUpdateMode( bOldUpdateMode );
881 if( aText.Len() )
883 maHFString.Append( '&' ).Append( cPortionCode ).Append( aText );
884 mnTotalHeight = ::std::max( mnTotalHeight, nHeight );
888 // URL conversion =============================================================
890 namespace {
892 /** Encodes special parts of the URL, i.e. directory separators and volume names.
893 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
894 rtl::OUString lclEncodeDosUrl(
895 XclBiff eBiff, const rtl::OUString& rUrl, const rtl::OUString& rBase, const rtl::OUString* pTableName)
897 rtl::OUStringBuffer aBuf;
899 if (!rUrl.isEmpty())
901 rtl::OUString aOldUrl = rUrl;
902 aBuf.append(EXC_URLSTART_ENCODED);
904 if ( aOldUrl.getLength() > 2 && aOldUrl.copy(0,2) == "\\\\" )
906 // UNC
907 aBuf.append(EXC_URL_DOSDRIVE).append(sal_Unicode('@'));
908 aOldUrl = aOldUrl.copy(2);
910 else if ( aOldUrl.getLength() > 2 && aOldUrl.copy(1,2) == ":\\" )
912 // drive letter
913 sal_Unicode cThisDrive = rBase.isEmpty() ? ' ' : rBase.getStr()[0];
914 sal_Unicode cDrive = aOldUrl.getStr()[0];
915 if (cThisDrive == cDrive)
916 // This document and the referenced document are under the same drive.
917 aBuf.append(EXC_URL_DRIVEROOT);
918 else
919 aBuf.append(EXC_URL_DOSDRIVE).append(cDrive);
920 aOldUrl = aOldUrl.copy(3);
923 // directories
924 sal_Int32 nPos = -1;
925 while((nPos = aOldUrl.indexOf('\\')) != -1)
927 if ( aOldUrl.copy(0,2) == ".." )
928 // parent dir (NOTE: the MS-XLS spec doesn't mention this, and
929 // Excel seems confused by this token).
930 aBuf.append(EXC_URL_PARENTDIR);
931 else
932 aBuf.append(aOldUrl.copy(0,nPos)).append(EXC_URL_SUBDIR);
934 aOldUrl = aOldUrl.copy(nPos + 1);
937 // file name
938 if (pTableName) // enclose file name in brackets if table name follows
939 aBuf.append(sal_Unicode('[')).append(aOldUrl).append(sal_Unicode(']'));
940 else
941 aBuf.append(aOldUrl);
943 else // empty URL -> self reference
945 switch( eBiff )
947 case EXC_BIFF5:
948 aBuf.append(pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF);
949 break;
950 case EXC_BIFF8:
951 DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" );
952 aBuf.append(EXC_URLSTART_SELF);
953 break;
954 default:
955 DBG_ERROR_BIFF();
959 // table name
960 if (pTableName)
961 aBuf.append(*pTableName);
963 return aBuf.makeStringAndClear();
966 } // namespace
968 // ----------------------------------------------------------------------------
970 rtl::OUString XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, const rtl::OUString& rAbsUrl, const rtl::OUString* pTableName )
972 rtl::OUString aDosUrl = INetURLObject(rAbsUrl).getFSysPath(INetURLObject::FSYS_DOS);
973 rtl::OUString aDosBase = INetURLObject(rRoot.GetBasePath()).getFSysPath(INetURLObject::FSYS_DOS);
974 return lclEncodeDosUrl(rRoot.GetBiff(), aDosUrl, aDosBase, pTableName);
977 rtl::OUString XclExpUrlHelper::EncodeDde( const rtl::OUString& rApplic, const rtl::OUString& rTopic )
979 rtl::OUStringBuffer aBuf;
980 aBuf.append(rApplic).append(EXC_DDE_DELIM).append(rTopic);
981 return aBuf.makeStringAndClear();
984 // Cached Value Lists =========================================================
986 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix )
987 : mrMatrix( rMatrix )
989 mrMatrix.IncRef();
991 XclExpCachedMatrix::~XclExpCachedMatrix()
993 mrMatrix.DecRef();
996 void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const
998 mrMatrix.GetDimensions( nCols, nRows );
1000 OSL_ENSURE( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" );
1001 OSL_ENSURE( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
1004 sal_Size XclExpCachedMatrix::GetSize() const
1006 SCSIZE nCols, nRows;
1008 GetDimensions( nCols, nRows );
1010 /* The returned size may be wrong if the matrix contains strings. The only
1011 effect is that the export stream has to update a wrong record size which is
1012 faster than to iterate through all cached values and calculate their sizes. */
1013 return 3 + 9 * (nCols * nRows);
1016 void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const
1018 SCSIZE nCols, nRows;
1020 GetDimensions( nCols, nRows );
1022 if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
1023 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1024 rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows );
1025 else
1026 // in BIFF8: columns and rows decreaed by 1
1027 rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 );
1029 for( SCSIZE nRow = 0; nRow < nRows; ++nRow )
1031 for( SCSIZE nCol = 0; nCol < nCols; ++nCol )
1033 ScMatrixValue nMatVal = mrMatrix.Get( nCol, nRow );
1035 if( SC_MATVAL_EMPTY == nMatVal.nType )
1037 rStrm.SetSliceSize( 9 );
1038 rStrm << EXC_CACHEDVAL_EMPTY;
1039 rStrm.WriteZeroBytes( 8 );
1041 else if( ScMatrix::IsNonValueType( nMatVal.nType ) )
1043 XclExpString aStr( nMatVal.GetString(), EXC_STR_DEFAULT );
1044 rStrm.SetSliceSize( 6 );
1045 rStrm << EXC_CACHEDVAL_STRING << aStr;
1047 else if( SC_MATVAL_BOOLEAN == nMatVal.nType )
1049 sal_Int8 nBool = nMatVal.GetBoolean();
1050 rStrm.SetSliceSize( 9 );
1051 rStrm << EXC_CACHEDVAL_BOOL << nBool;
1052 rStrm.WriteZeroBytes( 7 );
1054 else if( sal_uInt16 nScError = nMatVal.GetError() )
1056 sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) );
1057 rStrm.SetSliceSize( 9 );
1058 rStrm << EXC_CACHEDVAL_ERROR << nError;
1059 rStrm.WriteZeroBytes( 7 );
1061 else
1063 rStrm.SetSliceSize( 9 );
1064 rStrm << EXC_CACHEDVAL_DOUBLE << nMatVal.fVal;
1070 // ============================================================================
1072 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */