update ooo310-m15
[ooovba.git] / sc / source / filter / excel / xehelper.cxx
blob7dbe4b8867b7fc80d5180595fbe454679378c66b
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: xehelper.cxx,v $
10 * $Revision: 1.31.148.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
33 #include <com/sun/star/i18n/XBreakIterator.hpp>
34 #include <com/sun/star/i18n/ScriptType.hpp>
35 #include <sfx2/objsh.hxx>
36 #include <vcl/font.hxx>
37 #include <tools/urlobj.hxx>
38 #include <svtools/itemset.hxx>
39 #include <svtools/ctrltool.hxx>
40 #include <svx/svdotext.hxx>
41 #include <svx/outlobj.hxx>
42 #include "scitems.hxx"
43 #include <svx/fhgtitem.hxx>
44 #include <svx/flstitem.hxx>
45 #include <svx/colritem.hxx>
46 #include <svx/eeitem.hxx>
47 #include <svx/flditem.hxx>
48 #include <svx/escpitem.hxx>
49 #include <svx/svxfont.hxx>
51 #define _SVSTDARR_USHORTS
52 #include <svtools/svstdarr.hxx>
53 #include "document.hxx"
54 #include "docpool.hxx"
55 #include "cell.hxx"
56 #include "editutil.hxx"
57 #include "patattr.hxx"
58 #include "xestyle.hxx"
59 #include "fprogressbar.hxx"
60 #include "xltracer.hxx"
61 #include "xecontent.hxx"
62 #include "xelink.hxx"
63 #include "xehelper.hxx"
65 using ::rtl::OUString;
66 using ::com::sun::star::uno::Reference;
67 using ::com::sun::star::i18n::XBreakIterator;
69 // Export progress bar ========================================================
71 XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) :
72 XclExpRoot( rRoot ),
73 mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ),
74 mpSubProgress( 0 ),
75 mpSubRowCreate( 0 ),
76 mpSubRowFinal( 0 ),
77 mnSegRowFinal( SCF_INV_SEGMENT ),
78 mnRowCount( 0 )
82 XclExpProgressBar::~XclExpProgressBar()
86 void XclExpProgressBar::Initialize()
88 const ScDocument& rDoc = GetDoc();
89 const XclExpTabInfo& rTabInfo = GetTabInfo();
90 SCTAB nScTabCount = rTabInfo.GetScTabCount();
92 // *** segment: creation of ROW records *** -------------------------------
94 sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 );
95 mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate );
96 maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT );
98 for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab )
100 if( rTabInfo.IsExportTab( nScTab ) )
102 SCCOL nLastUsedScCol;
103 SCROW nLastUsedScRow;
104 rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
105 sal_Size nSegSize = static_cast< sal_Size >( nLastUsedScRow + 1 );
106 maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize );
110 // *** segment: writing all ROW records *** -------------------------------
112 mnSegRowFinal = mxProgress->AddSegment( 1000 );
113 // sub progress bar and segment are created later in ActivateFinalRowsSegment()
116 void XclExpProgressBar::IncRowRecordCount()
118 ++mnRowCount;
121 void XclExpProgressBar::ActivateCreateRowsSegment()
123 DBG_ASSERT( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
124 "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
125 sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ];
126 DBG_ASSERT( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
127 if( nSeg != SCF_INV_SEGMENT )
129 mpSubProgress = mpSubRowCreate;
130 mpSubProgress->ActivateSegment( nSeg );
132 else
133 mpSubProgress = 0;
136 void XclExpProgressBar::ActivateFinalRowsSegment()
138 if( !mpSubRowFinal && (mnRowCount > 0) )
140 mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal );
141 mpSubRowFinal->AddSegment( mnRowCount );
143 mpSubProgress = mpSubRowFinal;
144 if( mpSubProgress )
145 mpSubProgress->Activate();
148 void XclExpProgressBar::Progress()
150 if( mpSubProgress && !mpSubProgress->IsFull() )
151 mpSubProgress->Progress();
154 // Calc->Excel cell address/range conversion ==================================
156 namespace {
158 /** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
159 inline void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow )
161 rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol );
162 rXclPos.mnRow = static_cast< sal_uInt16 >( nScRow );
165 } // namespace
167 // ----------------------------------------------------------------------------
169 XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) :
170 XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() )
174 // cell address ---------------------------------------------------------------
176 bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn )
178 // ScAddress::operator<=() doesn't do what we want here
179 bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col());
180 bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row());
181 bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab());
183 bool bValid = bValidCol && bValidRow && bValidTab;
184 if( !bValid && bWarn )
186 mbColTrunc |= !bValidCol;
187 mbRowTrunc |= !bValidRow;
188 mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs
189 mrTracer.TraceInvalidAddress( rScPos, maMaxPos );
191 return bValid;
194 bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos,
195 const ScAddress& rScPos, bool bWarn )
197 bool bValid = CheckAddress( rScPos, bWarn );
198 if( bValid )
199 lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() );
200 return bValid;
203 XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn )
205 XclAddress aXclPos( ScAddress::UNINITIALIZED );
206 if( !ConvertAddress( aXclPos, rScPos, bWarn ) )
207 lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) );
208 return aXclPos;
211 // cell range -----------------------------------------------------------------
213 bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn )
215 return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn );
218 bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn )
220 rScRange.Justify();
222 // check start position
223 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
224 if( bValidStart )
226 // check & correct end position
227 ScAddress& rScEnd = rScRange.aEnd;
228 if( !CheckAddress( rScEnd, bWarn ) )
230 rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) );
231 rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) );
232 rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) );
236 return bValidStart;
239 bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange,
240 const ScRange& rScRange, bool bWarn )
242 // check start position
243 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
244 if( bValidStart )
246 lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() );
248 // check & correct end position
249 SCCOL nScCol2 = rScRange.aEnd.Col();
250 SCROW nScRow2 = rScRange.aEnd.Row();
251 if( !CheckAddress( rScRange.aEnd, bWarn ) )
253 nScCol2 = ::std::min( nScCol2, maMaxPos.Col() );
254 nScRow2 = ::std::min( nScRow2, maMaxPos.Row() );
256 lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 );
258 return bValidStart;
261 //UNUSED2008-05 XclRange XclExpAddressConverter::CreateValidRange( const ScRange& rScRange, bool bWarn )
262 //UNUSED2008-05 {
263 //UNUSED2008-05 return XclRange(
264 //UNUSED2008-05 CreateValidAddress( rScRange.aStart, bWarn ),
265 //UNUSED2008-05 CreateValidAddress( rScRange.aEnd, bWarn ) );
266 //UNUSED2008-05 }
268 // cell range list ------------------------------------------------------------
270 //UNUSED2008-05 bool XclExpAddressConverter::CheckRangeList( const ScRangeList& rScRanges, bool bWarn )
271 //UNUSED2008-05 {
272 //UNUSED2008-05 for( ULONG nIdx = 0, nSize = rScRanges.Count(); nIdx < nSize; ++nIdx )
273 //UNUSED2008-05 if( const ScRange* pScRange = rScRanges.GetObject( nIdx ) )
274 //UNUSED2008-05 if( !CheckRange( *pScRange, bWarn ) )
275 //UNUSED2008-05 return false;
276 //UNUSED2008-05 return true;
277 //UNUSED2008-05 }
279 void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn )
281 ULONG nIdx = rScRanges.Count();
282 while( nIdx )
284 --nIdx; // backwards to keep nIdx valid
285 ScRange* pScRange = rScRanges.GetObject( nIdx );
286 if( pScRange && !CheckRange( *pScRange, bWarn ) )
287 delete rScRanges.Remove( nIdx );
291 void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges,
292 const ScRangeList& rScRanges, bool bWarn )
294 rXclRanges.clear();
295 for( ULONG nPos = 0, nCount = rScRanges.Count(); nPos < nCount; ++nPos )
297 if( const ScRange* pScRange = rScRanges.GetObject( nPos ) )
299 XclRange aXclRange( ScAddress::UNINITIALIZED );
300 if( ConvertRange( aXclRange, *pScRange, bWarn ) )
301 rXclRanges.push_back( aXclRange );
306 // EditEngine->String conversion ==============================================
308 namespace {
310 String lclGetUrlRepresentation( const SvxURLField& rUrlField )
312 String aRepr( rUrlField.GetRepresentation() );
313 // no representation -> use URL
314 return aRepr.Len() ? aRepr : rUrlField.GetURL();
317 } // namespace
319 // ----------------------------------------------------------------------------
321 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
322 XclExpRoot( rRoot ),
323 maScPos( rScPos ),
324 mbMultipleUrls( false )
328 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
332 String XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField )
334 String aUrlRepr;
336 if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7
338 // there was/is already a HLINK record
339 mbMultipleUrls = mxLinkRec.is();
341 mxLinkRec.reset( new XclExpHyperlink( GetRoot(), rUrlField, maScPos ) );
343 if( const String* pRepr = mxLinkRec->GetRepr() )
344 aUrlRepr = *pRepr;
346 // add URL to note text
347 ScGlobal::AddToken( maUrlList, rUrlField.GetURL(), '\n' );
350 // no hyperlink representation from Excel HLINK record -> use it from text field
351 return aUrlRepr.Len() ? aUrlRepr : lclGetUrlRepresentation( rUrlField );
354 bool XclExpHyperlinkHelper::HasLinkRecord() const
356 return !mbMultipleUrls && mxLinkRec.is();
359 XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord()
361 if( HasLinkRecord() )
362 return mxLinkRec;
363 return XclExpHyperlinkRef();
366 // ----------------------------------------------------------------------------
368 namespace {
370 /** Creates a new formatted string from the passed unformatted string.
372 Creates a Unicode string or a byte string, depending on the current BIFF
373 version contained in the passed XclExpRoot object. May create a formatted
374 string object, if the text contains different script types.
376 @param pCellAttr
377 Cell attributes used for font formatting.
378 @param nFlags
379 Modifiers for string export.
380 @param nMaxLen
381 The maximum number of characters to store in this string.
382 @return
383 The new string object.
385 XclExpStringRef lclCreateFormattedString(
386 const XclExpRoot& rRoot, const String& rText, const ScPatternAttr* pCellAttr,
387 XclStrFlags nFlags, sal_uInt16 nMaxLen )
389 /* Create an empty Excel string object with correctly initialized BIFF mode,
390 because this function only uses Append() functions that require this. */
391 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
393 // script type handling
394 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
395 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
396 // #i63255# get script type for leading weak characters
397 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText );
399 // font buffer and cell item set
400 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
401 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
403 // process all script portions
404 OUString aOUText( rText );
405 sal_Int32 nPortionPos = 0;
406 sal_Int32 nTextLen = aOUText.getLength();
407 while( nPortionPos < nTextLen )
409 // get script type and end position of next script portion
410 sal_Int16 nScript = xBreakIt->getScriptType( aOUText, nPortionPos );
411 sal_Int32 nPortionEnd = xBreakIt->endOfScript( aOUText, nPortionPos, nScript );
413 // reuse previous script for following weak portions
414 if( nScript == ApiScriptType::WEAK )
415 nScript = nLastScript;
417 // construct font from current text portion
418 SvxFont aFont( XclExpFontBuffer::GetFontFromItemSet( rItemSet, nScript ) );
420 // Excel start position of this portion
421 sal_uInt16 nXclPortionStart = xString->Len();
422 // add portion text to Excel string
423 XclExpStringHelper::AppendString( *xString, rRoot, aOUText.copy( nPortionPos, nPortionEnd - nPortionPos ) );
424 if( nXclPortionStart < xString->Len() )
426 // insert font into buffer
427 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
428 // insert font index into format run vector
429 xString->AppendFormat( nXclPortionStart, nFontIdx );
432 // go to next script portion
433 nLastScript = nScript;
434 nPortionPos = nPortionEnd;
437 return xString;
440 /** Creates a new formatted string from an edit engine text object.
442 Creates a Unicode string or a byte string, depending on the current BIFF
443 version contained in the passed XclExpRoot object.
445 @param rEE
446 The edit engine in use. The text object must already be set.
447 @param nFlags
448 Modifiers for string export.
449 @param nMaxLen
450 The maximum number of characters to store in this string.
451 @return
452 The new string object.
454 XclExpStringRef lclCreateFormattedString(
455 const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper,
456 XclStrFlags nFlags, sal_uInt16 nMaxLen )
458 /* Create an empty Excel string object with correctly initialized BIFF mode,
459 because this function only uses Append() functions that require this. */
460 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
462 // font buffer and helper item set for edit engine -> Calc item conversion
463 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
464 SfxItemSet aItemSet( *rRoot.GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END );
466 // script type handling
467 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
468 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
469 // #i63255# get script type for leading weak characters
470 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() );
472 // process all paragraphs
473 sal_uInt16 nParaCount = rEE.GetParagraphCount();
474 for( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara )
476 ESelection aSel( nPara, 0 );
477 String aParaText( rEE.GetText( nPara ) );
479 SvUShorts aPosList;
480 rEE.GetPortions( nPara, aPosList );
482 // process all portions in the paragraph
483 sal_uInt16 nPosCount = aPosList.Count();
484 for( sal_uInt16 nPos = 0; nPos < nPosCount; ++nPos )
486 aSel.nEndPos = static_cast< xub_StrLen >( aPosList.GetObject( nPos ) );
487 String aXclPortionText( aParaText, aSel.nStartPos, aSel.nEndPos - aSel.nStartPos );
489 aItemSet.ClearItem();
490 SfxItemSet aEditSet( rEE.GetAttribs( aSel ) );
491 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
493 // get escapement value
494 short nEsc = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT ).GetEsc();
496 // process text fields
497 bool bIsHyperlink = false;
498 if( aSel.nStartPos + 1 == aSel.nEndPos )
500 // test if the character is a text field
501 const SfxPoolItem* pItem;
502 if( aEditSet.GetItemState( EE_FEATURE_FIELD, FALSE, &pItem ) == SFX_ITEM_SET )
504 const SvxFieldData* pField = static_cast< const SvxFieldItem* >( pItem )->GetField();
505 if( const SvxURLField* pUrlField = PTR_CAST( SvxURLField, pField ) )
507 // convert URL field to string representation
508 aXclPortionText = pLinkHelper ?
509 pLinkHelper->ProcessUrlField( *pUrlField ) :
510 lclGetUrlRepresentation( *pUrlField );
511 bIsHyperlink = true;
513 else
515 DBG_ERRORFILE( "lclCreateFormattedString - unknown text field" );
516 aXclPortionText.Erase();
521 // Excel start position of this portion
522 sal_uInt16 nXclPortionStart = xString->Len();
523 // add portion text to Excel string
524 XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText );
525 if( (nXclPortionStart < xString->Len()) || (aParaText.Len() == 0) )
527 /* Construct font from current edit engine text portion. Edit engine
528 creates different portions for different script types, no need to loop. */
529 sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 );
530 if( nScript == ApiScriptType::WEAK )
531 nScript = nLastScript;
532 SvxFont aFont( XclExpFontBuffer::GetFontFromItemSet( aItemSet, nScript ) );
533 nLastScript = nScript;
535 // add escapement
536 aFont.SetEscapement( nEsc );
537 // modify automatic font color for hyperlinks
538 if( bIsHyperlink && (GETITEM( aItemSet, SvxColorItem, ATTR_FONT_COLOR ).GetValue().GetColor() == COL_AUTO) )
539 aFont.SetColor( Color( COL_LIGHTBLUE ) );
541 // insert font into buffer
542 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
543 // insert font index into format run vector
544 xString->AppendFormat( nXclPortionStart, nFontIdx );
547 aSel.nStartPos = aSel.nEndPos;
550 // add trailing newline (important for correct character index calculation)
551 if( nPara + 1 < nParaCount )
552 XclExpStringHelper::AppendChar( *xString, rRoot, '\n' );
555 return xString;
558 } // namespace
560 // ----------------------------------------------------------------------------
562 XclExpStringRef XclExpStringHelper::CreateString(
563 const XclExpRoot& rRoot, const String& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
565 XclExpStringRef xString( new XclExpString );
566 if( rRoot.GetBiff() == EXC_BIFF8 )
567 xString->Assign( rString, nFlags, nMaxLen );
568 else
569 xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen );
570 return xString;
573 XclExpStringRef XclExpStringHelper::CreateString(
574 const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen )
576 XclExpStringRef xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
577 AppendChar( *xString, rRoot, cChar );
578 return xString;
581 void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, const String& rString )
583 if( rRoot.GetBiff() == EXC_BIFF8 )
584 rXclString.Append( rString );
585 else
586 rXclString.AppendByte( rString, rRoot.GetTextEncoding() );
589 void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar )
591 if( rRoot.GetBiff() == EXC_BIFF8 )
592 rXclString.Append( cChar );
593 else
594 rXclString.AppendByte( cChar, rRoot.GetTextEncoding() );
597 XclExpStringRef XclExpStringHelper::CreateCellString(
598 const XclExpRoot& rRoot, const ScStringCell& rStringCell, const ScPatternAttr* pCellAttr,
599 XclStrFlags nFlags, sal_uInt16 nMaxLen )
601 String aCellText;
602 rStringCell.GetString( aCellText );
603 XclExpStringRef xString = lclCreateFormattedString( rRoot, aCellText, pCellAttr, nFlags, nMaxLen );
604 if (rStringCell.HasPhonetic())
606 ScPhonetic aPhonetic;
607 static_cast<const ScAsianStringCell&>(rStringCell).GetPhonetic(aPhonetic);
608 if ( !aPhonetic.IsEmpty() )
609 xString->SetPhoneticSettings(aPhonetic);
612 return xString;
615 XclExpStringRef XclExpStringHelper::CreateCellString(
616 const XclExpRoot& rRoot, const ScEditCell& rEditCell, const ScPatternAttr* pCellAttr,
617 XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen )
619 XclExpStringRef xString;
620 if( const EditTextObject* pEditObj = rEditCell.GetData() )
622 // formatted cell
623 ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
624 BOOL bOldUpdateMode = rEE.GetUpdateMode();
625 rEE.SetUpdateMode( TRUE );
626 // default items
627 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
628 SfxItemSet* pEEItemSet = new SfxItemSet( rEE.GetEmptyItemSet() );
629 ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet );
630 rEE.SetDefaults( pEEItemSet ); // edit engine takes ownership
631 // create the string
632 rEE.SetText( *pEditObj );
633 xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen );
634 rEE.SetUpdateMode( bOldUpdateMode );
636 else
638 // unformatted cell
639 String aCellText;
640 rEditCell.GetString( aCellText );
641 xString = lclCreateFormattedString( rRoot, aCellText, pCellAttr, nFlags, nMaxLen );
644 if (rEditCell.HasPhonetic())
646 ScPhonetic aPhonetic;
647 static_cast<const ScAsianEditCell&>(rEditCell).GetPhonetic(aPhonetic);
648 if ( !aPhonetic.IsEmpty() )
649 xString->SetPhoneticSettings(aPhonetic);
652 return xString;
655 XclExpStringRef XclExpStringHelper::CreateString(
656 const XclExpRoot& rRoot, const SdrTextObj& rTextObj,
657 XclStrFlags nFlags, sal_uInt16 nMaxLen )
659 XclExpStringRef xString;
660 if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() )
662 EditEngine& rEE = rRoot.GetDrawEditEngine();
663 BOOL bOldUpdateMode = rEE.GetUpdateMode();
664 rEE.SetUpdateMode( TRUE );
665 // create the string
666 rEE.SetText( pParaObj->GetTextObject() );
667 xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen );
668 rEE.SetUpdateMode( bOldUpdateMode );
669 // limit formats - TODO: BIFF dependent
670 if( !xString->IsEmpty() )
672 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
673 xString->AppendTrailingFormat( EXC_FONT_APP );
676 else
678 DBG_ERRORFILE( "XclExpStringHelper::CreateString - textbox without para object" );
679 // create BIFF dependent empty Excel string
680 xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen );
682 return xString;
685 XclExpStringRef XclExpStringHelper::CreateString(
686 const XclExpRoot& rRoot, const EditTextObject& rEditObj,
687 XclStrFlags nFlags, sal_uInt16 nMaxLen )
689 XclExpStringRef xString;
690 EditEngine& rEE = rRoot.GetDrawEditEngine();
691 BOOL bOldUpdateMode = rEE.GetUpdateMode();
692 rEE.SetUpdateMode( TRUE );
693 rEE.SetText( rEditObj );
694 xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen );
695 rEE.SetUpdateMode( bOldUpdateMode );
696 // limit formats - TODO: BIFF dependent
697 if( !xString->IsEmpty() )
699 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
700 xString->AppendTrailingFormat( EXC_FONT_APP );
702 return xString;
705 sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const String& rString )
707 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
708 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
709 OUString aOUString( rString );
710 sal_Int32 nStrPos = 0;
711 sal_Int32 nStrLen = aOUString.getLength();
712 sal_Int16 nScript = ApiScriptType::WEAK;
713 while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) )
715 nScript = xBreakIt->getScriptType( aOUString, nStrPos );
716 nStrPos = xBreakIt->endOfScript( aOUString, nStrPos, nScript );
718 return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript;
721 // Header/footer conversion ===================================================
723 XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) :
724 XclExpRoot( rRoot ),
725 mrEE( rRoot.GetHFEditEngine() ),
726 mnTotalHeight( 0 )
730 void XclExpHFConverter::GenerateString(
731 const EditTextObject* pLeftObj,
732 const EditTextObject* pCenterObj,
733 const EditTextObject* pRightObj )
735 maHFString.Erase();
736 mnTotalHeight = 0;
737 AppendPortion( pLeftObj, 'L' );
738 AppendPortion( pCenterObj, 'C' );
739 AppendPortion( pRightObj, 'R' );
742 void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode )
744 if( !pTextObj ) return;
746 String aText;
747 sal_Int32 nHeight = 0;
748 SfxItemSet aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END );
750 // edit engine
751 BOOL bOldUpdateMode = mrEE.GetUpdateMode();
752 mrEE.SetUpdateMode( TRUE );
753 mrEE.SetText( *pTextObj );
755 // font information
756 XclFontData aFontData, aNewData;
757 if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
759 aFontData = pFirstFont->GetFontData();
760 (aFontData.mnHeight += 10) /= 20; // using pt here, not twips
762 else
763 aFontData.mnHeight = 10;
765 const FontList* pFontList = 0;
766 if( SfxObjectShell* pDocShell = GetDocShell() )
768 if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
769 pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
770 pFontList = pInfoItem->GetFontList();
773 sal_uInt16 nParaCount = mrEE.GetParagraphCount();
774 for( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara )
776 ESelection aSel( nPara, 0 );
777 String aParaText;
778 sal_Int32 nParaHeight = 0;
779 SvUShorts aPosList;
780 mrEE.GetPortions( nPara, aPosList );
782 sal_uInt16 nPosCount = aPosList.Count();
783 for( sal_uInt16 nPos = 0; nPos < nPosCount; ++nPos )
785 aSel.nEndPos = static_cast< xub_StrLen >( aPosList.GetObject( nPos ) );
786 if( aSel.nStartPos < aSel.nEndPos )
789 // --- font attributes ---
791 Font aFont;
792 aItemSet.ClearItem();
793 SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) );
794 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
795 ScPatternAttr::GetFont( aFont, aItemSet, SC_AUTOCOL_RAW );
797 // font name and style
798 aNewData.maName = XclTools::GetXclFontName( aFont.GetName() );
799 aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL;
800 aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE);
801 bool bNewFont = !(aFontData.maName == aNewData.maName);
802 bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) ||
803 (aFontData.mbItalic != aNewData.mbItalic);
804 if( bNewFont || (bNewStyle && pFontList) )
806 aParaText.AppendAscii( "&\"" ).Append( aNewData.maName );
807 if( pFontList )
809 FontInfo aFontInfo( pFontList->Get(
810 aNewData.maName,
811 (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL,
812 aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) );
813 aNewData.maStyle = pFontList->GetStyleName( aFontInfo );
814 if( aNewData.maStyle.Len() )
815 aParaText.Append( ',' ).Append( aNewData.maStyle );
817 aParaText.Append( '"' );
820 // height
821 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
822 // -> get it directly from edit engine item set
823 aNewData.mnHeight = ulimit_cast< sal_uInt16 >( GETITEM( aEditSet, SvxFontHeightItem, EE_CHAR_FONTHEIGHT ).GetHeight() );
824 (aNewData.mnHeight += 10) /= 20;
825 bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight);
826 if( bFontHtChanged )
827 aParaText.Append( '&' ).Append( String::CreateFromInt32( aNewData.mnHeight ) );
828 // update maximum paragraph height, convert to twips
829 nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 );
831 // underline
832 aNewData.mnUnderline = EXC_FONTUNDERL_NONE;
833 switch( aFont.GetUnderline() )
835 case UNDERLINE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break;
836 case UNDERLINE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break;
837 case UNDERLINE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
838 default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE;
840 if( aFontData.mnUnderline != aNewData.mnUnderline )
842 sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ?
843 aFontData.mnUnderline : aNewData.mnUnderline;
844 aParaText.AppendAscii( (nTmpUnderl == EXC_FONTUNDERL_SINGLE) ? "&U" : "&E" );
847 // strikeout
848 aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE);
849 if( aFontData.mbStrikeout != aNewData.mbStrikeout )
850 aParaText.AppendAscii( "&S" );
852 // super/sub script
853 const SvxEscapementItem& rEscapeItem = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT );
854 aNewData.SetScEscapement( rEscapeItem.GetEsc() );
855 if( aFontData.mnEscapem != aNewData.mnEscapem )
857 switch(aNewData.mnEscapem)
859 // close the previous super/sub script.
860 case EXC_FONTESC_NONE: aParaText.AppendAscii( (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? "&X" : "&Y" ); break;
861 case EXC_FONTESC_SUPER: aParaText.AppendAscii( "&X" ); break;
862 case EXC_FONTESC_SUB: aParaText.AppendAscii( "&Y" ); break;
863 default: break;
867 aFontData = aNewData;
869 // --- text content or text fields ---
871 const SfxPoolItem* pItem;
872 if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters
873 (aEditSet.GetItemState( EE_FEATURE_FIELD, sal_False, &pItem ) == SFX_ITEM_SET) )
875 if( const SvxFieldData* pFieldData = static_cast< const SvxFieldItem* >( pItem )->GetField() )
877 if( pFieldData->ISA( SvxPageField ) )
878 aParaText.AppendAscii( "&P" );
879 else if( pFieldData->ISA( SvxPagesField ) )
880 aParaText.AppendAscii( "&N" );
881 else if( pFieldData->ISA( SvxDateField ) )
882 aParaText.AppendAscii( "&D" );
883 else if( pFieldData->ISA( SvxTimeField ) || pFieldData->ISA( SvxExtTimeField ) )
884 aParaText.AppendAscii( "&T" );
885 else if( pFieldData->ISA( SvxTableField ) )
886 aParaText.AppendAscii( "&A" );
887 else if( pFieldData->ISA( SvxFileField ) ) // title -> file name
888 aParaText.AppendAscii( "&F" );
889 else if( const SvxExtFileField* pFileField = PTR_CAST( SvxExtFileField, pFieldData ) )
891 switch( pFileField->GetFormat() )
893 case SVXFILEFORMAT_NAME_EXT:
894 case SVXFILEFORMAT_NAME:
895 aParaText.AppendAscii( "&F" );
896 break;
897 case SVXFILEFORMAT_PATH:
898 aParaText.AppendAscii( "&Z" );
899 break;
900 case SVXFILEFORMAT_FULLPATH:
901 aParaText.AppendAscii( "&Z&F" );
902 break;
903 default:
904 DBG_ERRORFILE( "XclExpHFConverter::AppendPortion - unknown file field" );
909 else
911 String aPortionText( mrEE.GetText( aSel ) );
912 aPortionText.SearchAndReplaceAll( String( '&' ), String( RTL_CONSTASCII_USTRINGPARAM( "&&" ) ) );
913 // #i17440# space between font height and numbers in text
914 if( bFontHtChanged && aParaText.Len() && aPortionText.Len() )
916 sal_Unicode cLast = aParaText.GetChar( aParaText.Len() - 1 );
917 sal_Unicode cFirst = aPortionText.GetChar( 0 );
918 if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') )
919 aParaText.Append( ' ' );
921 aParaText.Append( aPortionText );
925 aSel.nStartPos = aSel.nEndPos;
928 ScGlobal::AddToken( aText, aParaText, '\n' );
929 if( nParaHeight == 0 )
930 nParaHeight = aFontData.mnHeight * 20; // points -> twips
931 nHeight += nParaHeight;
934 mrEE.SetUpdateMode( bOldUpdateMode );
936 if( aText.Len() )
938 maHFString.Append( '&' ).Append( cPortionCode ).Append( aText );
939 mnTotalHeight = ::std::max( mnTotalHeight, nHeight );
943 // URL conversion =============================================================
945 namespace {
947 /** Converts the file URL passed in rUrl to a URL in DOS notation (local or UNC).
948 @param rUrl (in/out-param) In: URL to convert; Out: Converted URL in DOS notation.
949 @param rBasePath Base path for relative URLs.
950 @param bSaveRelUrl Converts to a relative URL, using rBasePath.
951 @return True = Conversion successful, rUrl contains converted file URL. */
952 bool lclConvertToDos( String& rUrl, const String& rBasePath, bool bSaveRelUrl )
954 String aDosUrl( INetURLObject( rUrl ).getFSysPath( INetURLObject::FSYS_DOS ) );
955 bool bRet = (aDosUrl.Len() > 0);
956 if( bRet && bSaveRelUrl )
958 // try to convert to relative path
959 String aDosBase( INetURLObject( rBasePath ).getFSysPath( INetURLObject::FSYS_DOS ) );
960 if( aDosBase.Len() )
962 xub_StrLen nPos;
964 // --- 1st step: delete equal subdirectories ---
966 // special handling for UNC
967 xub_StrLen nStartSearch = aDosBase.EqualsAscii( "\\\\", 0, 2 ) ? 2 : 0;
968 bool bEqualBase = false;
969 bool bLoop = true;
970 while( bLoop && ((nPos = aDosBase.Search( '\\', nStartSearch )) != STRING_NOTFOUND) )
972 bLoop = (TRUE == aDosBase.Equals( aDosUrl, 0, nPos + 1 ));
973 if( bLoop )
975 aDosBase.Erase( 0, nPos + 1 );
976 aDosUrl.Erase( 0, nPos + 1 );
977 nStartSearch = 0;
978 bEqualBase = true;
982 // --- 2nd step: add parent directory levels ---
984 if( bEqualBase )
986 while( (nPos = aDosBase.Search( '\\' )) != STRING_NOTFOUND )
988 aDosBase.Erase( 0, nPos + 1 );
989 aDosUrl.InsertAscii( "..\\", 0 );
993 rUrl = aDosUrl;
995 return bRet;
998 /** Encodes special parts of the URL, i.e. directory separators and volume names.
999 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
1000 void lclEncodeDosUrl( XclBiff eBiff, String& rUrl, const String* pTableName = 0 )
1002 if( rUrl.Len() )
1004 String aOldUrl( rUrl );
1005 rUrl = EXC_URLSTART_ENCODED;
1007 if( (aOldUrl.Len() > 2) && aOldUrl.EqualsAscii( "\\\\", 0, 2 ) )
1009 // UNC
1010 rUrl.Append( EXC_URL_DOSDRIVE ).Append( '@' );
1011 aOldUrl.Erase( 0, 2 );
1013 else if( (aOldUrl.Len() > 2) && aOldUrl.EqualsAscii( ":\\", 1, 2 ) )
1015 // drive letter
1016 rUrl.Append( EXC_URL_DOSDRIVE ).Append( aOldUrl.GetChar( 0 ) );
1017 aOldUrl.Erase( 0, 3 );
1020 // directories
1021 xub_StrLen nPos;
1022 while( (nPos = aOldUrl.Search( '\\' )) != STRING_NOTFOUND )
1024 if( aOldUrl.EqualsAscii( "..", 0, 2 ) )
1025 rUrl.Append( EXC_URL_PARENTDIR ); // parent dir
1026 else
1027 rUrl.Append( aOldUrl.GetBuffer(), nPos ).Append( EXC_URL_SUBDIR );
1028 aOldUrl.Erase( 0, nPos + 1 );
1031 // file name
1032 if( pTableName ) // enclose file name in brackets if table name follows
1033 rUrl.Append( '[' ).Append( aOldUrl ).Append( ']' );
1034 else
1035 rUrl.Append( aOldUrl );
1037 else // empty URL -> self reference
1039 switch( eBiff )
1041 case EXC_BIFF5:
1042 rUrl = pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF;
1043 break;
1044 case EXC_BIFF8:
1045 DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" );
1046 rUrl = EXC_URLSTART_SELF;
1047 break;
1048 default:
1049 DBG_ERROR_BIFF();
1053 // table name
1054 if( pTableName )
1055 rUrl.Append( *pTableName );
1058 } // namespace
1060 // ----------------------------------------------------------------------------
1062 String XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, const String& rAbsUrl, const String* pTableName )
1064 String aDosUrl( rAbsUrl );
1065 if( !aDosUrl.Len() || lclConvertToDos( aDosUrl, rRoot.GetBasePath(), rRoot.IsRelUrl() ) )
1066 lclEncodeDosUrl( rRoot.GetBiff(), aDosUrl, pTableName );
1067 return aDosUrl;
1070 String XclExpUrlHelper::EncodeDde( const String& rApplic, const String rTopic )
1072 String aDde( rApplic );
1073 aDde.Append( EXC_DDE_DELIM ).Append( rTopic );
1074 return aDde;
1077 // Cached Value Lists =========================================================
1079 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix )
1080 : mrMatrix( rMatrix )
1082 mrMatrix.IncRef();
1084 XclExpCachedMatrix::~XclExpCachedMatrix()
1086 mrMatrix.DecRef();
1089 void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const
1091 mrMatrix.GetDimensions( nCols, nRows );
1093 DBG_ASSERT( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" );
1094 DBG_ASSERT( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
1097 sal_Size XclExpCachedMatrix::GetSize() const
1099 SCSIZE nCols, nRows;
1101 GetDimensions( nCols, nRows );
1103 /* The returned size may be wrong if the matrix contains strings. The only
1104 effect is that the export stream has to update a wrong record size which is
1105 faster than to iterate through all cached values and calculate their sizes. */
1106 return 3 + 9 * (nCols * nRows);
1109 void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const
1111 SCSIZE nCols, nRows;
1113 GetDimensions( nCols, nRows );
1115 if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
1116 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1117 rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows );
1118 else
1119 // in BIFF8: columns and rows decreaed by 1
1120 rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 );
1122 for( SCSIZE nRow = 0; nRow < nRows; ++nRow )
1124 for( SCSIZE nCol = 0; nCol < nCols; ++nCol )
1126 ScMatValType nMatValType = SC_MATVAL_VALUE;
1127 const ScMatrixValue* pMatVal = mrMatrix.Get( nCol, nRow, nMatValType );
1129 if( !pMatVal || SC_MATVAL_EMPTY == nMatValType )
1131 rStrm.SetSliceSize( 9 );
1132 rStrm << EXC_CACHEDVAL_EMPTY;
1133 rStrm.WriteZeroBytes( 8 );
1135 else if( ScMatrix::IsNonValueType( nMatValType ) )
1137 XclExpString aStr( pMatVal->GetString(), EXC_STR_DEFAULT );
1138 rStrm.SetSliceSize( 6 );
1139 rStrm << EXC_CACHEDVAL_STRING << aStr;
1141 else if( SC_MATVAL_BOOLEAN == nMatValType )
1143 sal_Int8 nBool = pMatVal->GetBoolean();
1144 rStrm.SetSliceSize( 9 );
1145 rStrm << EXC_CACHEDVAL_BOOL << nBool;
1146 rStrm.WriteZeroBytes( 7 );
1148 else if( USHORT nScError = pMatVal->GetError() )
1150 sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) );
1151 rStrm.SetSliceSize( 9 );
1152 rStrm << EXC_CACHEDVAL_ERROR << nError;
1153 rStrm.WriteZeroBytes( 7 );
1155 else
1157 rStrm.SetSliceSize( 9 );
1158 rStrm << EXC_CACHEDVAL_DOUBLE << pMatVal->fVal;
1164 // ============================================================================