1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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"
56 #include "editutil.hxx"
57 #include "patattr.hxx"
58 #include "xestyle.hxx"
59 #include "fprogressbar.hxx"
60 #include "xltracer.hxx"
61 #include "xecontent.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
) :
73 mxProgress( new ScfProgressBar( rRoot
.GetDocShell(), STR_SAVE_DOC
) ),
77 mnSegRowFinal( SCF_INV_SEGMENT
),
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()
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
);
136 void XclExpProgressBar::ActivateFinalRowsSegment()
138 if( !mpSubRowFinal
&& (mnRowCount
> 0) )
140 mpSubRowFinal
= &mxProgress
->GetSegmentProgressBar( mnSegRowFinal
);
141 mpSubRowFinal
->AddSegment( mnRowCount
);
143 mpSubProgress
= mpSubRowFinal
;
145 mpSubProgress
->Activate();
148 void XclExpProgressBar::Progress()
150 if( mpSubProgress
&& !mpSubProgress
->IsFull() )
151 mpSubProgress
->Progress();
154 // Calc->Excel cell address/range conversion ==================================
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
);
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
);
194 bool XclExpAddressConverter::ConvertAddress( XclAddress
& rXclPos
,
195 const ScAddress
& rScPos
, bool bWarn
)
197 bool bValid
= CheckAddress( rScPos
, bWarn
);
199 lclFillAddress( rXclPos
, rScPos
.Col(), rScPos
.Row() );
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() ) );
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
)
222 // check start position
223 bool bValidStart
= CheckAddress( rScRange
.aStart
, bWarn
);
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() ) );
239 bool XclExpAddressConverter::ConvertRange( XclRange
& rXclRange
,
240 const ScRange
& rScRange
, bool bWarn
)
242 // check start position
243 bool bValidStart
= CheckAddress( rScRange
.aStart
, bWarn
);
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
);
261 //UNUSED2008-05 XclRange XclExpAddressConverter::CreateValidRange( const ScRange& rScRange, bool bWarn )
263 //UNUSED2008-05 return XclRange(
264 //UNUSED2008-05 CreateValidAddress( rScRange.aStart, bWarn ),
265 //UNUSED2008-05 CreateValidAddress( rScRange.aEnd, bWarn ) );
268 // cell range list ------------------------------------------------------------
270 //UNUSED2008-05 bool XclExpAddressConverter::CheckRangeList( const ScRangeList& rScRanges, bool bWarn )
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;
279 void XclExpAddressConverter::ValidateRangeList( ScRangeList
& rScRanges
, bool bWarn
)
281 ULONG nIdx
= rScRanges
.Count();
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
)
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 ==============================================
310 String
lclGetUrlRepresentation( const SvxURLField
& rUrlField
)
312 String
aRepr( rUrlField
.GetRepresentation() );
313 // no representation -> use URL
314 return aRepr
.Len() ? aRepr
: rUrlField
.GetURL();
319 // ----------------------------------------------------------------------------
321 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot
& rRoot
, const ScAddress
& rScPos
) :
324 mbMultipleUrls( false )
328 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
332 String
XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField
& rUrlField
)
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() )
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() )
363 return XclExpHyperlinkRef();
366 // ----------------------------------------------------------------------------
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.
377 Cell attributes used for font formatting.
379 Modifiers for string export.
381 The maximum number of characters to store in this string.
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
;
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.
446 The edit engine in use. The text object must already be set.
448 Modifiers for string export.
450 The maximum number of characters to store in this string.
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
) );
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
);
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
;
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' );
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
);
569 xString
->AssignByte( rString
, rRoot
.GetTextEncoding(), nFlags
, nMaxLen
);
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
);
581 void XclExpStringHelper::AppendString( XclExpString
& rXclString
, const XclExpRoot
& rRoot
, const String
& rString
)
583 if( rRoot
.GetBiff() == EXC_BIFF8
)
584 rXclString
.Append( rString
);
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
);
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
)
602 rStringCell
.GetString( aCellText
);
603 return lclCreateFormattedString( rRoot
, aCellText
, pCellAttr
, nFlags
, nMaxLen
);
606 XclExpStringRef
XclExpStringHelper::CreateCellString(
607 const XclExpRoot
& rRoot
, const ScEditCell
& rEditCell
, const ScPatternAttr
* pCellAttr
,
608 XclExpHyperlinkHelper
& rLinkHelper
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
610 XclExpStringRef xString
;
611 if( const EditTextObject
* pEditObj
= rEditCell
.GetData() )
614 ScEditEngineDefaulter
& rEE
= rRoot
.GetEditEngine();
615 BOOL bOldUpdateMode
= rEE
.GetUpdateMode();
616 rEE
.SetUpdateMode( TRUE
);
618 const SfxItemSet
& rItemSet
= pCellAttr
? pCellAttr
->GetItemSet() : rRoot
.GetDoc().GetDefPattern()->GetItemSet();
619 SfxItemSet
* pEEItemSet
= new SfxItemSet( rEE
.GetEmptyItemSet() );
620 ScPatternAttr::FillToEditItemSet( *pEEItemSet
, rItemSet
);
621 rEE
.SetDefaults( pEEItemSet
); // edit engine takes ownership
623 rEE
.SetText( *pEditObj
);
624 xString
= lclCreateFormattedString( rRoot
, rEE
, &rLinkHelper
, nFlags
, nMaxLen
);
625 rEE
.SetUpdateMode( bOldUpdateMode
);
631 rEditCell
.GetString( aCellText
);
632 xString
= lclCreateFormattedString( rRoot
, aCellText
, pCellAttr
, nFlags
, nMaxLen
);
637 XclExpStringRef
XclExpStringHelper::CreateString(
638 const XclExpRoot
& rRoot
, const SdrTextObj
& rTextObj
,
639 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
641 XclExpStringRef xString
;
642 if( const OutlinerParaObject
* pParaObj
= rTextObj
.GetOutlinerParaObject() )
644 EditEngine
& rEE
= rRoot
.GetDrawEditEngine();
645 BOOL bOldUpdateMode
= rEE
.GetUpdateMode();
646 rEE
.SetUpdateMode( TRUE
);
648 rEE
.SetText( pParaObj
->GetTextObject() );
649 xString
= lclCreateFormattedString( rRoot
, rEE
, 0, nFlags
, nMaxLen
);
650 rEE
.SetUpdateMode( bOldUpdateMode
);
651 // limit formats - TODO: BIFF dependent
652 if( !xString
->IsEmpty() )
654 xString
->LimitFormatCount( EXC_MAXRECSIZE_BIFF8
/ 8 - 1 );
655 xString
->AppendTrailingFormat( EXC_FONT_APP
);
660 DBG_ERRORFILE( "XclExpStringHelper::CreateString - textbox without para object" );
661 // create BIFF dependent empty Excel string
662 xString
= CreateString( rRoot
, EMPTY_STRING
, nFlags
, nMaxLen
);
667 XclExpStringRef
XclExpStringHelper::CreateString(
668 const XclExpRoot
& rRoot
, const EditTextObject
& rEditObj
,
669 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
671 XclExpStringRef xString
;
672 EditEngine
& rEE
= rRoot
.GetDrawEditEngine();
673 BOOL bOldUpdateMode
= rEE
.GetUpdateMode();
674 rEE
.SetUpdateMode( TRUE
);
675 rEE
.SetText( rEditObj
);
676 xString
= lclCreateFormattedString( rRoot
, rEE
, 0, nFlags
, nMaxLen
);
677 rEE
.SetUpdateMode( bOldUpdateMode
);
678 // limit formats - TODO: BIFF dependent
679 if( !xString
->IsEmpty() )
681 xString
->LimitFormatCount( EXC_MAXRECSIZE_BIFF8
/ 8 - 1 );
682 xString
->AppendTrailingFormat( EXC_FONT_APP
);
687 sal_Int16
XclExpStringHelper::GetLeadingScriptType( const XclExpRoot
& rRoot
, const String
& rString
)
689 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
690 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
691 OUString
aOUString( rString
);
692 sal_Int32 nStrPos
= 0;
693 sal_Int32 nStrLen
= aOUString
.getLength();
694 sal_Int16 nScript
= ApiScriptType::WEAK
;
695 while( (nStrPos
< nStrLen
) && (nScript
== ApiScriptType::WEAK
) )
697 nScript
= xBreakIt
->getScriptType( aOUString
, nStrPos
);
698 nStrPos
= xBreakIt
->endOfScript( aOUString
, nStrPos
, nScript
);
700 return (nScript
== ApiScriptType::WEAK
) ? rRoot
.GetDefApiScript() : nScript
;
703 // Header/footer conversion ===================================================
705 XclExpHFConverter::XclExpHFConverter( const XclExpRoot
& rRoot
) :
707 mrEE( rRoot
.GetHFEditEngine() ),
712 void XclExpHFConverter::GenerateString(
713 const EditTextObject
* pLeftObj
,
714 const EditTextObject
* pCenterObj
,
715 const EditTextObject
* pRightObj
)
719 AppendPortion( pLeftObj
, 'L' );
720 AppendPortion( pCenterObj
, 'C' );
721 AppendPortion( pRightObj
, 'R' );
724 void XclExpHFConverter::AppendPortion( const EditTextObject
* pTextObj
, sal_Unicode cPortionCode
)
726 if( !pTextObj
) return;
729 sal_Int32 nHeight
= 0;
730 SfxItemSet
aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START
, ATTR_PATTERN_END
);
733 BOOL bOldUpdateMode
= mrEE
.GetUpdateMode();
734 mrEE
.SetUpdateMode( TRUE
);
735 mrEE
.SetText( *pTextObj
);
738 XclFontData aFontData
, aNewData
;
739 if( const XclExpFont
* pFirstFont
= GetFontBuffer().GetFont( EXC_FONT_APP
) )
741 aFontData
= pFirstFont
->GetFontData();
742 (aFontData
.mnHeight
+= 10) /= 20; // using pt here, not twips
745 aFontData
.mnHeight
= 10;
747 const FontList
* pFontList
= 0;
748 if( SfxObjectShell
* pDocShell
= GetDocShell() )
750 if( const SvxFontListItem
* pInfoItem
= static_cast< const SvxFontListItem
* >(
751 pDocShell
->GetItem( SID_ATTR_CHAR_FONTLIST
) ) )
752 pFontList
= pInfoItem
->GetFontList();
755 sal_uInt16 nParaCount
= mrEE
.GetParagraphCount();
756 for( sal_uInt16 nPara
= 0; nPara
< nParaCount
; ++nPara
)
758 ESelection
aSel( nPara
, 0 );
760 sal_Int32 nParaHeight
= 0;
762 mrEE
.GetPortions( nPara
, aPosList
);
764 sal_uInt16 nPosCount
= aPosList
.Count();
765 for( sal_uInt16 nPos
= 0; nPos
< nPosCount
; ++nPos
)
767 aSel
.nEndPos
= static_cast< xub_StrLen
>( aPosList
.GetObject( nPos
) );
768 if( aSel
.nStartPos
< aSel
.nEndPos
)
771 // --- font attributes ---
774 aItemSet
.ClearItem();
775 SfxItemSet
aEditSet( mrEE
.GetAttribs( aSel
) );
776 ScPatternAttr::GetFromEditItemSet( aItemSet
, aEditSet
);
777 ScPatternAttr::GetFont( aFont
, aItemSet
, SC_AUTOCOL_RAW
);
779 // font name and style
780 aNewData
.maName
= XclTools::GetXclFontName( aFont
.GetName() );
781 aNewData
.mnWeight
= (aFont
.GetWeight() > WEIGHT_NORMAL
) ? EXC_FONTWGHT_BOLD
: EXC_FONTWGHT_NORMAL
;
782 aNewData
.mbItalic
= (aFont
.GetItalic() != ITALIC_NONE
);
783 bool bNewFont
= !(aFontData
.maName
== aNewData
.maName
);
784 bool bNewStyle
= (aFontData
.mnWeight
!= aNewData
.mnWeight
) ||
785 (aFontData
.mbItalic
!= aNewData
.mbItalic
);
786 if( bNewFont
|| (bNewStyle
&& pFontList
) )
788 aParaText
.AppendAscii( "&\"" ).Append( aNewData
.maName
);
791 FontInfo
aFontInfo( pFontList
->Get(
793 (aNewData
.mnWeight
> EXC_FONTWGHT_NORMAL
) ? WEIGHT_BOLD
: WEIGHT_NORMAL
,
794 aNewData
.mbItalic
? ITALIC_NORMAL
: ITALIC_NONE
) );
795 aNewData
.maStyle
= pFontList
->GetStyleName( aFontInfo
);
796 if( aNewData
.maStyle
.Len() )
797 aParaText
.Append( ',' ).Append( aNewData
.maStyle
);
799 aParaText
.Append( '"' );
803 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
804 // -> get it directly from edit engine item set
805 aNewData
.mnHeight
= ulimit_cast
< sal_uInt16
>( GETITEM( aEditSet
, SvxFontHeightItem
, EE_CHAR_FONTHEIGHT
).GetHeight() );
806 (aNewData
.mnHeight
+= 10) /= 20;
807 bool bFontHtChanged
= (aFontData
.mnHeight
!= aNewData
.mnHeight
);
809 aParaText
.Append( '&' ).Append( String::CreateFromInt32( aNewData
.mnHeight
) );
810 // update maximum paragraph height, convert to twips
811 nParaHeight
= ::std::max
< sal_Int32
>( nParaHeight
, aNewData
.mnHeight
* 20 );
814 aNewData
.mnUnderline
= EXC_FONTUNDERL_NONE
;
815 switch( aFont
.GetUnderline() )
817 case UNDERLINE_NONE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_NONE
; break;
818 case UNDERLINE_SINGLE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_SINGLE
; break;
819 case UNDERLINE_DOUBLE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_DOUBLE
; break;
820 default: aNewData
.mnUnderline
= EXC_FONTUNDERL_SINGLE
;
822 if( aFontData
.mnUnderline
!= aNewData
.mnUnderline
)
824 sal_uInt8 nTmpUnderl
= (aNewData
.mnUnderline
== EXC_FONTUNDERL_NONE
) ?
825 aFontData
.mnUnderline
: aNewData
.mnUnderline
;
826 aParaText
.AppendAscii( (nTmpUnderl
== EXC_FONTUNDERL_SINGLE
) ? "&U" : "&E" );
830 aNewData
.mbStrikeout
= (aFont
.GetStrikeout() != STRIKEOUT_NONE
);
831 if( aFontData
.mbStrikeout
!= aNewData
.mbStrikeout
)
832 aParaText
.AppendAscii( "&S" );
835 const SvxEscapementItem
& rEscapeItem
= GETITEM( aEditSet
, SvxEscapementItem
, EE_CHAR_ESCAPEMENT
);
836 aNewData
.SetScEscapement( rEscapeItem
.GetEsc() );
837 if( aFontData
.mnEscapem
!= aNewData
.mnEscapem
)
839 switch(aNewData
.mnEscapem
)
841 // close the previous super/sub script.
842 case EXC_FONTESC_NONE
: aParaText
.AppendAscii( (aFontData
.mnEscapem
== EXC_FONTESC_SUPER
) ? "&X" : "&Y" ); break;
843 case EXC_FONTESC_SUPER
: aParaText
.AppendAscii( "&X" ); break;
844 case EXC_FONTESC_SUB
: aParaText
.AppendAscii( "&Y" ); break;
849 aFontData
= aNewData
;
851 // --- text content or text fields ---
853 const SfxPoolItem
* pItem
;
854 if( (aSel
.nStartPos
+ 1 == aSel
.nEndPos
) && // fields are single characters
855 (aEditSet
.GetItemState( EE_FEATURE_FIELD
, sal_False
, &pItem
) == SFX_ITEM_SET
) )
857 if( const SvxFieldData
* pFieldData
= static_cast< const SvxFieldItem
* >( pItem
)->GetField() )
859 if( pFieldData
->ISA( SvxPageField
) )
860 aParaText
.AppendAscii( "&P" );
861 else if( pFieldData
->ISA( SvxPagesField
) )
862 aParaText
.AppendAscii( "&N" );
863 else if( pFieldData
->ISA( SvxDateField
) )
864 aParaText
.AppendAscii( "&D" );
865 else if( pFieldData
->ISA( SvxTimeField
) || pFieldData
->ISA( SvxExtTimeField
) )
866 aParaText
.AppendAscii( "&T" );
867 else if( pFieldData
->ISA( SvxTableField
) )
868 aParaText
.AppendAscii( "&A" );
869 else if( pFieldData
->ISA( SvxFileField
) ) // title -> file name
870 aParaText
.AppendAscii( "&F" );
871 else if( const SvxExtFileField
* pFileField
= PTR_CAST( SvxExtFileField
, pFieldData
) )
873 switch( pFileField
->GetFormat() )
875 case SVXFILEFORMAT_NAME_EXT
:
876 case SVXFILEFORMAT_NAME
:
877 aParaText
.AppendAscii( "&F" );
879 case SVXFILEFORMAT_PATH
:
880 aParaText
.AppendAscii( "&Z" );
882 case SVXFILEFORMAT_FULLPATH
:
883 aParaText
.AppendAscii( "&Z&F" );
886 DBG_ERRORFILE( "XclExpHFConverter::AppendPortion - unknown file field" );
893 String
aPortionText( mrEE
.GetText( aSel
) );
894 aPortionText
.SearchAndReplaceAll( String( '&' ), String( RTL_CONSTASCII_USTRINGPARAM( "&&" ) ) );
895 // #i17440# space between font height and numbers in text
896 if( bFontHtChanged
&& aParaText
.Len() && aPortionText
.Len() )
898 sal_Unicode cLast
= aParaText
.GetChar( aParaText
.Len() - 1 );
899 sal_Unicode cFirst
= aPortionText
.GetChar( 0 );
900 if( ('0' <= cLast
) && (cLast
<= '9') && ('0' <= cFirst
) && (cFirst
<= '9') )
901 aParaText
.Append( ' ' );
903 aParaText
.Append( aPortionText
);
907 aSel
.nStartPos
= aSel
.nEndPos
;
910 ScGlobal::AddToken( aText
, aParaText
, '\n' );
911 if( nParaHeight
== 0 )
912 nParaHeight
= aFontData
.mnHeight
* 20; // points -> twips
913 nHeight
+= nParaHeight
;
916 mrEE
.SetUpdateMode( bOldUpdateMode
);
920 maHFString
.Append( '&' ).Append( cPortionCode
).Append( aText
);
921 mnTotalHeight
= ::std::max( mnTotalHeight
, nHeight
);
925 // URL conversion =============================================================
929 /** Converts the file URL passed in rUrl to a URL in DOS notation (local or UNC).
930 @param rUrl (in/out-param) In: URL to convert; Out: Converted URL in DOS notation.
931 @param rBasePath Base path for relative URLs.
932 @param bSaveRelUrl Converts to a relative URL, using rBasePath.
933 @return True = Conversion successful, rUrl contains converted file URL. */
934 bool lclConvertToDos( String
& rUrl
, const String
& rBasePath
, bool bSaveRelUrl
)
936 String
aDosUrl( INetURLObject( rUrl
).getFSysPath( INetURLObject::FSYS_DOS
) );
937 bool bRet
= (aDosUrl
.Len() > 0);
938 if( bRet
&& bSaveRelUrl
)
940 // try to convert to relative path
941 String
aDosBase( INetURLObject( rBasePath
).getFSysPath( INetURLObject::FSYS_DOS
) );
946 // --- 1st step: delete equal subdirectories ---
948 // special handling for UNC
949 xub_StrLen nStartSearch
= aDosBase
.EqualsAscii( "\\\\", 0, 2 ) ? 2 : 0;
950 bool bEqualBase
= false;
952 while( bLoop
&& ((nPos
= aDosBase
.Search( '\\', nStartSearch
)) != STRING_NOTFOUND
) )
954 bLoop
= (TRUE
== aDosBase
.Equals( aDosUrl
, 0, nPos
+ 1 ));
957 aDosBase
.Erase( 0, nPos
+ 1 );
958 aDosUrl
.Erase( 0, nPos
+ 1 );
964 // --- 2nd step: add parent directory levels ---
968 while( (nPos
= aDosBase
.Search( '\\' )) != STRING_NOTFOUND
)
970 aDosBase
.Erase( 0, nPos
+ 1 );
971 aDosUrl
.InsertAscii( "..\\", 0 );
980 /** Encodes special parts of the URL, i.e. directory separators and volume names.
981 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
982 void lclEncodeDosUrl( XclBiff eBiff
, String
& rUrl
, const String
* pTableName
= 0 )
986 String
aOldUrl( rUrl
);
987 rUrl
= EXC_URLSTART_ENCODED
;
989 if( (aOldUrl
.Len() > 2) && aOldUrl
.EqualsAscii( "\\\\", 0, 2 ) )
992 rUrl
.Append( EXC_URL_DOSDRIVE
).Append( '@' );
993 aOldUrl
.Erase( 0, 2 );
995 else if( (aOldUrl
.Len() > 2) && aOldUrl
.EqualsAscii( ":\\", 1, 2 ) )
998 rUrl
.Append( EXC_URL_DOSDRIVE
).Append( aOldUrl
.GetChar( 0 ) );
999 aOldUrl
.Erase( 0, 3 );
1004 while( (nPos
= aOldUrl
.Search( '\\' )) != STRING_NOTFOUND
)
1006 if( aOldUrl
.EqualsAscii( "..", 0, 2 ) )
1007 rUrl
.Append( EXC_URL_PARENTDIR
); // parent dir
1009 rUrl
.Append( aOldUrl
.GetBuffer(), nPos
).Append( EXC_URL_SUBDIR
);
1010 aOldUrl
.Erase( 0, nPos
+ 1 );
1014 if( pTableName
) // enclose file name in brackets if table name follows
1015 rUrl
.Append( '[' ).Append( aOldUrl
).Append( ']' );
1017 rUrl
.Append( aOldUrl
);
1019 else // empty URL -> self reference
1024 rUrl
= pTableName
? EXC_URLSTART_SELFENCODED
: EXC_URLSTART_SELF
;
1027 DBG_ASSERT( pTableName
, "lclEncodeDosUrl - sheet name required for BIFF8" );
1028 rUrl
= EXC_URLSTART_SELF
;
1037 rUrl
.Append( *pTableName
);
1042 // ----------------------------------------------------------------------------
1044 String
XclExpUrlHelper::EncodeUrl( const XclExpRoot
& rRoot
, const String
& rAbsUrl
, const String
* pTableName
)
1046 String
aDosUrl( rAbsUrl
);
1047 if( !aDosUrl
.Len() || lclConvertToDos( aDosUrl
, rRoot
.GetBasePath(), rRoot
.IsRelUrl() ) )
1048 lclEncodeDosUrl( rRoot
.GetBiff(), aDosUrl
, pTableName
);
1052 String
XclExpUrlHelper::EncodeDde( const String
& rApplic
, const String rTopic
)
1054 String
aDde( rApplic
);
1055 aDde
.Append( EXC_DDE_DELIM
).Append( rTopic
);
1059 // Cached Value Lists =========================================================
1061 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix
& rMatrix
)
1062 : mrMatrix( rMatrix
)
1066 XclExpCachedMatrix::~XclExpCachedMatrix()
1071 void XclExpCachedMatrix::GetDimensions( SCSIZE
& nCols
, SCSIZE
& nRows
) const
1073 mrMatrix
.GetDimensions( nCols
, nRows
);
1075 DBG_ASSERT( nCols
&& nRows
, "XclExpCachedMatrix::GetDimensions - empty matrix" );
1076 DBG_ASSERT( nCols
<= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
1079 sal_Size
XclExpCachedMatrix::GetSize() const
1081 SCSIZE nCols
, nRows
;
1083 GetDimensions( nCols
, nRows
);
1085 /* The returned size may be wrong if the matrix contains strings. The only
1086 effect is that the export stream has to update a wrong record size which is
1087 faster than to iterate through all cached values and calculate their sizes. */
1088 return 3 + 9 * (nCols
* nRows
);
1091 void XclExpCachedMatrix::Save( XclExpStream
& rStrm
) const
1093 SCSIZE nCols
, nRows
;
1095 GetDimensions( nCols
, nRows
);
1097 if( rStrm
.GetRoot().GetBiff() <= EXC_BIFF5
)
1098 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1099 rStrm
<< static_cast< sal_uInt8
>( nCols
) << static_cast< sal_uInt16
>( nRows
);
1101 // in BIFF8: columns and rows decreaed by 1
1102 rStrm
<< static_cast< sal_uInt8
>( nCols
- 1 ) << static_cast< sal_uInt16
>( nRows
- 1 );
1104 for( SCSIZE nRow
= 0; nRow
< nRows
; ++nRow
)
1106 for( SCSIZE nCol
= 0; nCol
< nCols
; ++nCol
)
1108 ScMatValType nMatValType
= SC_MATVAL_VALUE
;
1109 const ScMatrixValue
* pMatVal
= mrMatrix
.Get( nCol
, nRow
, nMatValType
);
1111 if( !pMatVal
|| SC_MATVAL_EMPTY
== nMatValType
)
1113 rStrm
.SetSliceSize( 9 );
1114 rStrm
<< EXC_CACHEDVAL_EMPTY
;
1115 rStrm
.WriteZeroBytes( 8 );
1117 else if( ScMatrix::IsNonValueType( nMatValType
) )
1119 XclExpString
aStr( pMatVal
->GetString(), EXC_STR_DEFAULT
);
1120 rStrm
.SetSliceSize( 6 );
1121 rStrm
<< EXC_CACHEDVAL_STRING
<< aStr
;
1123 else if( SC_MATVAL_BOOLEAN
== nMatValType
)
1125 sal_Int8 nBool
= pMatVal
->GetBoolean();
1126 rStrm
.SetSliceSize( 9 );
1127 rStrm
<< EXC_CACHEDVAL_BOOL
<< nBool
;
1128 rStrm
.WriteZeroBytes( 7 );
1130 else if( USHORT nScError
= pMatVal
->GetError() )
1132 sal_Int8
nError ( XclTools::GetXclErrorCode( nScError
) );
1133 rStrm
.SetSliceSize( 9 );
1134 rStrm
<< EXC_CACHEDVAL_ERROR
<< nError
;
1135 rStrm
.WriteZeroBytes( 7 );
1139 rStrm
.SetSliceSize( 9 );
1140 rStrm
<< EXC_CACHEDVAL_DOUBLE
<< pMatVal
->fVal
;
1146 // ============================================================================