1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/i18n/XBreakIterator.hpp>
21 #include <com/sun/star/i18n/ScriptType.hpp>
22 #include <sfx2/objsh.hxx>
23 #include <vcl/font.hxx>
24 #include <tools/urlobj.hxx>
25 #include <svl/itemset.hxx>
26 #include <svtools/ctrltool.hxx>
27 #include <svx/svdotext.hxx>
28 #include <editeng/outlobj.hxx>
29 #include "scitems.hxx"
30 #include <editeng/fhgtitem.hxx>
31 #include <editeng/flstitem.hxx>
32 #include <editeng/colritem.hxx>
33 #include <editeng/eeitem.hxx>
34 #include <editeng/flditem.hxx>
35 #include <editeng/escapementitem.hxx>
36 #include <editeng/svxfont.hxx>
38 #include "document.hxx"
39 #include "docpool.hxx"
40 #include "formulacell.hxx"
41 #include "editutil.hxx"
42 #include "patattr.hxx"
43 #include "scmatrix.hxx"
44 #include "xestyle.hxx"
45 #include "fprogressbar.hxx"
46 #include "xltracer.hxx"
47 #include "xecontent.hxx"
49 #include "xehelper.hxx"
51 using ::com::sun::star::uno::Reference
;
52 using ::com::sun::star::i18n::XBreakIterator
;
54 // Export progress bar ========================================================
56 XclExpProgressBar::XclExpProgressBar( const XclExpRoot
& rRoot
) :
58 mxProgress( new ScfProgressBar( rRoot
.GetDocShell(), STR_SAVE_DOC
) ),
62 mnSegRowFinal( SCF_INV_SEGMENT
),
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
) )
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()
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
);
121 void XclExpProgressBar::ActivateFinalRowsSegment()
123 if( !mpSubRowFinal
&& (mnRowCount
> 0) )
125 mpSubRowFinal
= &mxProgress
->GetSegmentProgressBar( mnSegRowFinal
);
126 mpSubRowFinal
->AddSegment( mnRowCount
);
128 mpSubProgress
= mpSubRowFinal
;
130 mpSubProgress
->Activate();
133 void XclExpProgressBar::Progress()
135 if( mpSubProgress
&& !mpSubProgress
->IsFull() )
136 mpSubProgress
->Progress();
139 // Calc->Excel cell address/range conversion ==================================
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
);
152 XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot
& rRoot
) :
153 XclAddressConverterBase( rRoot
.GetTracer(), rRoot
.GetXclMaxPos() )
157 // cell address ---------------------------------------------------------------
159 bool XclExpAddressConverter::CheckAddress( const ScAddress
& rScPos
, bool bWarn
)
161 // ScAddress::operator<=() doesn't do what we want here
162 bool bValidCol
= (0 <= rScPos
.Col()) && (rScPos
.Col() <= maMaxPos
.Col());
163 bool bValidRow
= (0 <= rScPos
.Row()) && (rScPos
.Row() <= maMaxPos
.Row());
164 bool bValidTab
= (0 <= rScPos
.Tab()) && (rScPos
.Tab() <= maMaxPos
.Tab());
166 bool bValid
= bValidCol
&& bValidRow
&& bValidTab
;
169 mbColTrunc
|= !bValidCol
;
170 mbRowTrunc
|= !bValidRow
;
172 if( !bValid
&& bWarn
)
174 mbTabTrunc
|= (rScPos
.Tab() > maMaxPos
.Tab()); // do not warn for deleted refs
175 mrTracer
.TraceInvalidAddress( rScPos
, maMaxPos
);
180 bool XclExpAddressConverter::ConvertAddress( XclAddress
& rXclPos
,
181 const ScAddress
& rScPos
, bool bWarn
)
183 bool bValid
= CheckAddress( rScPos
, bWarn
);
185 lclFillAddress( rXclPos
, rScPos
.Col(), rScPos
.Row() );
189 XclAddress
XclExpAddressConverter::CreateValidAddress( const ScAddress
& rScPos
, bool bWarn
)
191 XclAddress
aXclPos( ScAddress::UNINITIALIZED
);
192 if( !ConvertAddress( aXclPos
, rScPos
, bWarn
) )
193 lclFillAddress( aXclPos
, ::std::min( rScPos
.Col(), maMaxPos
.Col() ), ::std::min( rScPos
.Row(), maMaxPos
.Row() ) );
197 // cell range -----------------------------------------------------------------
199 bool XclExpAddressConverter::CheckRange( const ScRange
& rScRange
, bool bWarn
)
201 return CheckAddress( rScRange
.aStart
, bWarn
) && CheckAddress( rScRange
.aEnd
, bWarn
);
204 bool XclExpAddressConverter::ValidateRange( ScRange
& rScRange
, bool bWarn
)
208 // check start position
209 bool bValidStart
= CheckAddress( rScRange
.aStart
, bWarn
);
212 // check & correct end position
213 ScAddress
& rScEnd
= rScRange
.aEnd
;
214 if( !CheckAddress( rScEnd
, bWarn
) )
216 rScEnd
.SetCol( ::std::min( rScEnd
.Col(), maMaxPos
.Col() ) );
217 rScEnd
.SetRow( ::std::min( rScEnd
.Row(), maMaxPos
.Row() ) );
218 rScEnd
.SetTab( ::std::min( rScEnd
.Tab(), maMaxPos
.Tab() ) );
225 bool XclExpAddressConverter::ConvertRange( XclRange
& rXclRange
,
226 const ScRange
& rScRange
, bool bWarn
)
228 // check start position
229 bool bValidStart
= CheckAddress( rScRange
.aStart
, bWarn
);
232 lclFillAddress( rXclRange
.maFirst
, rScRange
.aStart
.Col(), rScRange
.aStart
.Row() );
234 // check & correct end position
235 SCCOL nScCol2
= rScRange
.aEnd
.Col();
236 SCROW nScRow2
= rScRange
.aEnd
.Row();
237 if( !CheckAddress( rScRange
.aEnd
, bWarn
) )
239 nScCol2
= ::std::min( nScCol2
, maMaxPos
.Col() );
240 nScRow2
= ::std::min( nScRow2
, maMaxPos
.Row() );
242 lclFillAddress( rXclRange
.maLast
, nScCol2
, nScRow2
);
247 // cell range list ------------------------------------------------------------
249 void XclExpAddressConverter::ValidateRangeList( ScRangeList
& rScRanges
, bool bWarn
)
251 for ( size_t nRange
= rScRanges
.size(); nRange
> 0; )
253 ScRange
* pScRange
= rScRanges
[ --nRange
];
254 if( !CheckRange( *pScRange
, bWarn
) )
255 delete rScRanges
.Remove(nRange
);
259 void XclExpAddressConverter::ConvertRangeList( XclRangeList
& rXclRanges
,
260 const ScRangeList
& rScRanges
, bool bWarn
)
263 for( size_t nPos
= 0, nCount
= rScRanges
.size(); nPos
< nCount
; ++nPos
)
265 if( const ScRange
* pScRange
= rScRanges
[ nPos
] )
267 XclRange
aXclRange( ScAddress::UNINITIALIZED
);
268 if( ConvertRange( aXclRange
, *pScRange
, bWarn
) )
269 rXclRanges
.push_back( aXclRange
);
274 // EditEngine->String conversion ==============================================
278 OUString
lclGetUrlRepresentation( const SvxURLField
& rUrlField
)
280 const OUString
& aRepr
= rUrlField
.GetRepresentation();
281 // no representation -> use URL
282 return aRepr
.isEmpty() ? rUrlField
.GetURL() : aRepr
;
287 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot
& rRoot
, const ScAddress
& rScPos
) :
290 mbMultipleUrls( false )
294 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
298 OUString
XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField
& rUrlField
)
302 if( GetBiff() == EXC_BIFF8
) // no HLINK records in BIFF2-BIFF7
304 // there was/is already a HLINK record
305 mbMultipleUrls
= static_cast< bool >(mxLinkRec
);
307 mxLinkRec
.reset( new XclExpHyperlink( GetRoot(), rUrlField
, maScPos
) );
309 if( const OUString
* pRepr
= mxLinkRec
->GetRepr() )
312 // add URL to note text
313 maUrlList
= ScGlobal::addToken( maUrlList
, rUrlField
.GetURL(), '\n' );
316 // no hyperlink representation from Excel HLINK record -> use it from text field
317 return aUrlRepr
.isEmpty() ? lclGetUrlRepresentation(rUrlField
) : aUrlRepr
;
320 bool XclExpHyperlinkHelper::HasLinkRecord() const
322 return !mbMultipleUrls
&& mxLinkRec
;
325 XclExpHyperlinkHelper::XclExpHyperlinkRef
XclExpHyperlinkHelper::GetLinkRecord()
327 if( HasLinkRecord() )
329 return XclExpHyperlinkRef();
334 /** Creates a new formatted string from the passed unformatted string.
336 Creates a Unicode string or a byte string, depending on the current BIFF
337 version contained in the passed XclExpRoot object. May create a formatted
338 string object, if the text contains different script types.
341 Cell attributes used for font formatting.
343 Modifiers for string export.
345 The maximum number of characters to store in this string.
347 The new string object.
349 XclExpStringRef
lclCreateFormattedString(
350 const XclExpRoot
& rRoot
, const OUString
& rText
, const ScPatternAttr
* pCellAttr
,
351 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
353 /* Create an empty Excel string object with correctly initialized BIFF mode,
354 because this function only uses Append() functions that require this. */
355 XclExpStringRef xString
= XclExpStringHelper::CreateString( rRoot
, EMPTY_OUSTRING
, nFlags
, nMaxLen
);
357 // script type handling
358 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
359 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
360 // #i63255# get script type for leading weak characters
361 sal_Int16 nLastScript
= XclExpStringHelper::GetLeadingScriptType( rRoot
, rText
);
363 // font buffer and cell item set
364 XclExpFontBuffer
& rFontBuffer
= rRoot
.GetFontBuffer();
365 const SfxItemSet
& rItemSet
= pCellAttr
? pCellAttr
->GetItemSet() : rRoot
.GetDoc().GetDefPattern()->GetItemSet();
367 // process all script portions
368 OUString
aOUText( rText
);
369 sal_Int32 nPortionPos
= 0;
370 sal_Int32 nTextLen
= aOUText
.getLength();
371 while( nPortionPos
< nTextLen
)
373 // get script type and end position of next script portion
374 sal_Int16 nScript
= xBreakIt
->getScriptType( aOUText
, nPortionPos
);
375 sal_Int32 nPortionEnd
= xBreakIt
->endOfScript( aOUText
, nPortionPos
, nScript
);
377 // reuse previous script for following weak portions
378 if( nScript
== ApiScriptType::WEAK
)
379 nScript
= nLastScript
;
381 // construct font from current text portion
382 SvxFont
aFont( XclExpFontHelper::GetFontFromItemSet( rRoot
, rItemSet
, nScript
) );
384 // Excel start position of this portion
385 sal_Int32 nXclPortionStart
= xString
->Len();
386 // add portion text to Excel string
387 XclExpStringHelper::AppendString( *xString
, rRoot
, aOUText
.copy( nPortionPos
, nPortionEnd
- nPortionPos
) );
388 if( nXclPortionStart
< xString
->Len() )
390 // insert font into buffer
391 sal_uInt16 nFontIdx
= rFontBuffer
.Insert( aFont
, EXC_COLOR_CELLTEXT
);
392 // insert font index into format run vector
393 xString
->AppendFormat( nXclPortionStart
, nFontIdx
);
396 // go to next script portion
397 nLastScript
= nScript
;
398 nPortionPos
= nPortionEnd
;
404 /** Creates a new formatted string from an edit engine text object.
406 Creates a Unicode string or a byte string, depending on the current BIFF
407 version contained in the passed XclExpRoot object.
410 The edit engine in use. The text object must already be set.
412 Modifiers for string export.
414 The maximum number of characters to store in this string.
416 The new string object.
418 XclExpStringRef
lclCreateFormattedString(
419 const XclExpRoot
& rRoot
, EditEngine
& rEE
, XclExpHyperlinkHelper
* pLinkHelper
,
420 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
422 /* Create an empty Excel string object with correctly initialized BIFF mode,
423 because this function only uses Append() functions that require this. */
424 XclExpStringRef xString
= XclExpStringHelper::CreateString( rRoot
, EMPTY_OUSTRING
, nFlags
, nMaxLen
);
426 // font buffer and helper item set for edit engine -> Calc item conversion
427 XclExpFontBuffer
& rFontBuffer
= rRoot
.GetFontBuffer();
428 SfxItemSet
aItemSet( *rRoot
.GetDoc().GetPool(), ATTR_PATTERN_START
, ATTR_PATTERN_END
);
430 // script type handling
431 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
432 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
433 // #i63255# get script type for leading weak characters
434 sal_Int16 nLastScript
= XclExpStringHelper::GetLeadingScriptType( rRoot
, rEE
.GetText() );
436 // process all paragraphs
437 sal_Int32 nParaCount
= rEE
.GetParagraphCount();
438 for( sal_Int32 nPara
= 0; nPara
< nParaCount
; ++nPara
)
440 ESelection
aSel( nPara
, 0 );
441 OUString
aParaText( rEE
.GetText( nPara
) );
443 std::vector
<sal_Int32
> aPosList
;
444 rEE
.GetPortions( nPara
, aPosList
);
446 // process all portions in the paragraph
447 for( std::vector
<sal_Int32
>::const_iterator
it(aPosList
.begin()); it
!= aPosList
.end(); ++it
)
450 OUString aXclPortionText
= aParaText
.copy( aSel
.nStartPos
, aSel
.nEndPos
- aSel
.nStartPos
);
452 aItemSet
.ClearItem();
453 SfxItemSet
aEditSet( rEE
.GetAttribs( aSel
) );
454 ScPatternAttr::GetFromEditItemSet( aItemSet
, aEditSet
);
456 // get escapement value
457 short nEsc
= GETITEM( aEditSet
, SvxEscapementItem
, EE_CHAR_ESCAPEMENT
).GetEsc();
459 // process text fields
460 bool bIsHyperlink
= false;
461 if( aSel
.nStartPos
+ 1 == aSel
.nEndPos
)
463 // test if the character is a text field
464 const SfxPoolItem
* pItem
;
465 if( aEditSet
.GetItemState( EE_FEATURE_FIELD
, false, &pItem
) == SfxItemState::SET
)
467 const SvxFieldData
* pField
= static_cast< const SvxFieldItem
* >( pItem
)->GetField();
468 if( const SvxURLField
* pUrlField
= PTR_CAST( SvxURLField
, pField
) )
470 // convert URL field to string representation
471 aXclPortionText
= pLinkHelper
?
472 pLinkHelper
->ProcessUrlField( *pUrlField
) :
473 lclGetUrlRepresentation( *pUrlField
);
478 OSL_FAIL( "lclCreateFormattedString - unknown text field" );
479 aXclPortionText
.clear();
484 // Excel start position of this portion
485 sal_Int32 nXclPortionStart
= xString
->Len();
486 // add portion text to Excel string
487 XclExpStringHelper::AppendString( *xString
, rRoot
, aXclPortionText
);
488 if( (nXclPortionStart
< xString
->Len()) || (aParaText
.isEmpty()) )
490 /* Construct font from current edit engine text portion. Edit engine
491 creates different portions for different script types, no need to loop. */
492 sal_Int16 nScript
= xBreakIt
->getScriptType( aXclPortionText
, 0 );
493 if( nScript
== ApiScriptType::WEAK
)
494 nScript
= nLastScript
;
495 SvxFont
aFont( XclExpFontHelper::GetFontFromItemSet( rRoot
, aItemSet
, nScript
) );
496 nLastScript
= nScript
;
499 aFont
.SetEscapement( nEsc
);
500 // modify automatic font color for hyperlinks
501 if( bIsHyperlink
&& (GETITEM( aItemSet
, SvxColorItem
, ATTR_FONT_COLOR
).GetValue().GetColor() == COL_AUTO
) )
502 aFont
.SetColor( Color( COL_LIGHTBLUE
) );
504 // insert font into buffer
505 sal_uInt16 nFontIdx
= rFontBuffer
.Insert( aFont
, EXC_COLOR_CELLTEXT
);
506 // insert font index into format run vector
507 xString
->AppendFormat( nXclPortionStart
, nFontIdx
);
510 aSel
.nStartPos
= aSel
.nEndPos
;
513 // add trailing newline (important for correct character index calculation)
514 if( nPara
+ 1 < nParaCount
)
515 XclExpStringHelper::AppendChar( *xString
, rRoot
, '\n' );
523 XclExpStringRef
XclExpStringHelper::CreateString(
524 const XclExpRoot
& rRoot
, const OUString
& rString
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
526 XclExpStringRef
xString( new XclExpString
);
527 if( rRoot
.GetBiff() == EXC_BIFF8
)
528 xString
->Assign( rString
, nFlags
, nMaxLen
);
530 xString
->AssignByte( rString
, rRoot
.GetTextEncoding(), nFlags
, nMaxLen
);
534 XclExpStringRef
XclExpStringHelper::CreateString(
535 const XclExpRoot
& rRoot
, sal_Unicode cChar
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
537 XclExpStringRef xString
= CreateString( rRoot
, EMPTY_OUSTRING
, nFlags
, nMaxLen
);
538 AppendChar( *xString
, rRoot
, cChar
);
542 void XclExpStringHelper::AppendString( XclExpString
& rXclString
, const XclExpRoot
& rRoot
, const OUString
& rString
)
544 if( rRoot
.GetBiff() == EXC_BIFF8
)
545 rXclString
.Append( rString
);
547 rXclString
.AppendByte( rString
, rRoot
.GetTextEncoding() );
550 void XclExpStringHelper::AppendChar( XclExpString
& rXclString
, const XclExpRoot
& rRoot
, sal_Unicode cChar
)
552 if( rRoot
.GetBiff() == EXC_BIFF8
)
553 rXclString
.Append( OUString(cChar
) );
555 rXclString
.AppendByte( cChar
, rRoot
.GetTextEncoding() );
558 XclExpStringRef
XclExpStringHelper::CreateCellString(
559 const XclExpRoot
& rRoot
, const OUString
& rString
, const ScPatternAttr
* pCellAttr
,
560 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
562 return lclCreateFormattedString(rRoot
, rString
, pCellAttr
, nFlags
, nMaxLen
);
565 XclExpStringRef
XclExpStringHelper::CreateCellString(
566 const XclExpRoot
& rRoot
, const EditTextObject
& rEditText
, const ScPatternAttr
* pCellAttr
,
567 XclExpHyperlinkHelper
& rLinkHelper
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
569 XclExpStringRef xString
;
572 ScEditEngineDefaulter
& rEE
= rRoot
.GetEditEngine();
573 bool bOldUpdateMode
= rEE
.GetUpdateMode();
574 rEE
.SetUpdateMode( true );
577 const SfxItemSet
& rItemSet
= pCellAttr
? pCellAttr
->GetItemSet() : rRoot
.GetDoc().GetDefPattern()->GetItemSet();
578 SfxItemSet
* pEEItemSet
= new SfxItemSet( rEE
.GetEmptyItemSet() );
579 ScPatternAttr::FillToEditItemSet( *pEEItemSet
, rItemSet
);
580 rEE
.SetDefaults( pEEItemSet
); // edit engine takes ownership
583 rEE
.SetText(rEditText
);
584 xString
= lclCreateFormattedString( rRoot
, rEE
, &rLinkHelper
, nFlags
, nMaxLen
);
585 rEE
.SetUpdateMode( bOldUpdateMode
);
590 XclExpStringRef
XclExpStringHelper::CreateString(
591 const XclExpRoot
& rRoot
, const SdrTextObj
& rTextObj
,
592 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
594 XclExpStringRef xString
;
595 if( const OutlinerParaObject
* pParaObj
= rTextObj
.GetOutlinerParaObject() )
597 EditEngine
& rEE
= rRoot
.GetDrawEditEngine();
598 bool bOldUpdateMode
= rEE
.GetUpdateMode();
599 rEE
.SetUpdateMode( true );
601 rEE
.SetText( pParaObj
->GetTextObject() );
602 xString
= lclCreateFormattedString( rRoot
, rEE
, 0, nFlags
, nMaxLen
);
603 rEE
.SetUpdateMode( bOldUpdateMode
);
604 // limit formats - TODO: BIFF dependent
605 if( !xString
->IsEmpty() )
607 xString
->LimitFormatCount( EXC_MAXRECSIZE_BIFF8
/ 8 - 1 );
608 xString
->AppendTrailingFormat( EXC_FONT_APP
);
613 OSL_FAIL( "XclExpStringHelper::CreateString - textbox without para object" );
614 // create BIFF dependent empty Excel string
615 xString
= CreateString( rRoot
, EMPTY_OUSTRING
, nFlags
, nMaxLen
);
620 XclExpStringRef
XclExpStringHelper::CreateString(
621 const XclExpRoot
& rRoot
, const EditTextObject
& rEditObj
,
622 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
624 XclExpStringRef xString
;
625 EditEngine
& rEE
= rRoot
.GetDrawEditEngine();
626 bool bOldUpdateMode
= rEE
.GetUpdateMode();
627 rEE
.SetUpdateMode( true );
628 rEE
.SetText( rEditObj
);
629 xString
= lclCreateFormattedString( rRoot
, rEE
, 0, nFlags
, nMaxLen
);
630 rEE
.SetUpdateMode( bOldUpdateMode
);
631 // limit formats - TODO: BIFF dependent
632 if( !xString
->IsEmpty() )
634 xString
->LimitFormatCount( EXC_MAXRECSIZE_BIFF8
/ 8 - 1 );
635 xString
->AppendTrailingFormat( EXC_FONT_APP
);
640 sal_Int16
XclExpStringHelper::GetLeadingScriptType( const XclExpRoot
& rRoot
, const OUString
& rString
)
642 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
643 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
644 OUString
aOUString( rString
);
645 sal_Int32 nStrPos
= 0;
646 sal_Int32 nStrLen
= aOUString
.getLength();
647 sal_Int16 nScript
= ApiScriptType::WEAK
;
648 while( (nStrPos
< nStrLen
) && (nScript
== ApiScriptType::WEAK
) )
650 nScript
= xBreakIt
->getScriptType( aOUString
, nStrPos
);
651 nStrPos
= xBreakIt
->endOfScript( aOUString
, nStrPos
, nScript
);
653 return (nScript
== ApiScriptType::WEAK
) ? rRoot
.GetDefApiScript() : nScript
;
656 // Header/footer conversion ===================================================
658 XclExpHFConverter::XclExpHFConverter( const XclExpRoot
& rRoot
) :
660 mrEE( rRoot
.GetHFEditEngine() ),
665 void XclExpHFConverter::GenerateString(
666 const EditTextObject
* pLeftObj
,
667 const EditTextObject
* pCenterObj
,
668 const EditTextObject
* pRightObj
)
672 AppendPortion( pLeftObj
, 'L' );
673 AppendPortion( pCenterObj
, 'C' );
674 AppendPortion( pRightObj
, 'R' );
677 void XclExpHFConverter::AppendPortion( const EditTextObject
* pTextObj
, sal_Unicode cPortionCode
)
679 if( !pTextObj
) return;
682 sal_Int32 nHeight
= 0;
683 SfxItemSet
aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START
, ATTR_PATTERN_END
);
686 bool bOldUpdateMode
= mrEE
.GetUpdateMode();
687 mrEE
.SetUpdateMode( true );
688 mrEE
.SetText( *pTextObj
);
691 XclFontData aFontData
, aNewData
;
692 if( const XclExpFont
* pFirstFont
= GetFontBuffer().GetFont( EXC_FONT_APP
) )
694 aFontData
= pFirstFont
->GetFontData();
695 (aFontData
.mnHeight
+= 10) /= 20; // using pt here, not twips
698 aFontData
.mnHeight
= 10;
700 const FontList
* pFontList
= 0;
701 if( SfxObjectShell
* pDocShell
= GetDocShell() )
703 if( const SvxFontListItem
* pInfoItem
= static_cast< const SvxFontListItem
* >(
704 pDocShell
->GetItem( SID_ATTR_CHAR_FONTLIST
) ) )
705 pFontList
= pInfoItem
->GetFontList();
708 sal_Int32 nParaCount
= mrEE
.GetParagraphCount();
709 for( sal_Int32 nPara
= 0; nPara
< nParaCount
; ++nPara
)
711 ESelection
aSel( nPara
, 0 );
713 sal_Int32 nParaHeight
= 0;
714 std::vector
<sal_Int32
> aPosList
;
715 mrEE
.GetPortions( nPara
, aPosList
);
717 for( std::vector
<sal_Int32
>::const_iterator
it( aPosList
.begin() ); it
!= aPosList
.end(); ++it
)
720 if( aSel
.nStartPos
< aSel
.nEndPos
)
723 // --- font attributes ---
726 aItemSet
.ClearItem();
727 SfxItemSet
aEditSet( mrEE
.GetAttribs( aSel
) );
728 ScPatternAttr::GetFromEditItemSet( aItemSet
, aEditSet
);
729 ScPatternAttr::GetFont( aFont
, aItemSet
, SC_AUTOCOL_RAW
);
731 // font name and style
732 aNewData
.maName
= XclTools::GetXclFontName( aFont
.GetName() );
733 aNewData
.mnWeight
= (aFont
.GetWeight() > WEIGHT_NORMAL
) ? EXC_FONTWGHT_BOLD
: EXC_FONTWGHT_NORMAL
;
734 aNewData
.mbItalic
= (aFont
.GetItalic() != ITALIC_NONE
);
735 bool bNewFont
= !(aFontData
.maName
== aNewData
.maName
);
736 bool bNewStyle
= (aFontData
.mnWeight
!= aNewData
.mnWeight
) ||
737 (aFontData
.mbItalic
!= aNewData
.mbItalic
);
738 if( bNewFont
|| (bNewStyle
&& pFontList
) )
740 aParaText
= "&\"" + OUString(aNewData
.maName
);
743 vcl::FontInfo
aFontInfo( pFontList
->Get(
745 (aNewData
.mnWeight
> EXC_FONTWGHT_NORMAL
) ? WEIGHT_BOLD
: WEIGHT_NORMAL
,
746 aNewData
.mbItalic
? ITALIC_NORMAL
: ITALIC_NONE
) );
747 aNewData
.maStyle
= pFontList
->GetStyleName( aFontInfo
);
748 if( !aNewData
.maStyle
.isEmpty() )
749 aParaText
+= "," + aNewData
.maStyle
;
755 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
756 // -> get it directly from edit engine item set
757 aNewData
.mnHeight
= ulimit_cast
< sal_uInt16
>( GETITEM( aEditSet
, SvxFontHeightItem
, EE_CHAR_FONTHEIGHT
).GetHeight() );
758 (aNewData
.mnHeight
+= 10) /= 20;
759 bool bFontHtChanged
= (aFontData
.mnHeight
!= aNewData
.mnHeight
);
761 aParaText
+= "&" + OUString::number( aNewData
.mnHeight
);
762 // update maximum paragraph height, convert to twips
763 nParaHeight
= ::std::max
< sal_Int32
>( nParaHeight
, aNewData
.mnHeight
* 20 );
766 aNewData
.mnUnderline
= EXC_FONTUNDERL_NONE
;
767 switch( aFont
.GetUnderline() )
769 case UNDERLINE_NONE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_NONE
; break;
770 case UNDERLINE_SINGLE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_SINGLE
; break;
771 case UNDERLINE_DOUBLE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_DOUBLE
; break;
772 default: aNewData
.mnUnderline
= EXC_FONTUNDERL_SINGLE
;
774 if( aFontData
.mnUnderline
!= aNewData
.mnUnderline
)
776 sal_uInt8 nTmpUnderl
= (aNewData
.mnUnderline
== EXC_FONTUNDERL_NONE
) ?
777 aFontData
.mnUnderline
: aNewData
.mnUnderline
;
778 (nTmpUnderl
== EXC_FONTUNDERL_SINGLE
)? aParaText
+= "&U" : aParaText
+= "&E";
782 aNewData
.mbStrikeout
= (aFont
.GetStrikeout() != STRIKEOUT_NONE
);
783 if( aFontData
.mbStrikeout
!= aNewData
.mbStrikeout
)
787 const SvxEscapementItem
& rEscapeItem
= GETITEM( aEditSet
, SvxEscapementItem
, EE_CHAR_ESCAPEMENT
);
788 aNewData
.SetScEscapement( rEscapeItem
.GetEsc() );
789 if( aFontData
.mnEscapem
!= aNewData
.mnEscapem
)
791 switch(aNewData
.mnEscapem
)
793 // close the previous super/sub script.
794 case EXC_FONTESC_NONE
: (aFontData
.mnEscapem
== EXC_FONTESC_SUPER
) ? aParaText
+= "&X" : aParaText
+= "&Y"; break;
795 case EXC_FONTESC_SUPER
: aParaText
+= "&X"; break;
796 case EXC_FONTESC_SUB
: aParaText
+= "&Y"; break;
801 aFontData
= aNewData
;
803 // --- text content or text fields ---
805 const SfxPoolItem
* pItem
;
806 if( (aSel
.nStartPos
+ 1 == aSel
.nEndPos
) && // fields are single characters
807 (aEditSet
.GetItemState( EE_FEATURE_FIELD
, false, &pItem
) == SfxItemState::SET
) )
809 if( const SvxFieldData
* pFieldData
= static_cast< const SvxFieldItem
* >( pItem
)->GetField() )
811 if( pFieldData
->ISA( SvxPageField
) )
813 else if( pFieldData
->ISA( SvxPagesField
) )
815 else if( pFieldData
->ISA( SvxDateField
) )
817 else if( pFieldData
->ISA( SvxTimeField
) || pFieldData
->ISA( SvxExtTimeField
) )
819 else if( pFieldData
->ISA( SvxTableField
) )
821 else if( pFieldData
->ISA( SvxFileField
) ) // title -> file name
823 else if( const SvxExtFileField
* pFileField
= PTR_CAST( SvxExtFileField
, pFieldData
) )
825 switch( pFileField
->GetFormat() )
827 case SVXFILEFORMAT_NAME_EXT
:
828 case SVXFILEFORMAT_NAME
:
831 case SVXFILEFORMAT_PATH
:
834 case SVXFILEFORMAT_FULLPATH
:
838 OSL_FAIL( "XclExpHFConverter::AppendPortion - unknown file field" );
845 OUString
aPortionText( mrEE
.GetText( aSel
) );
846 aPortionText
= aPortionText
.replaceAll( "&", "&&" );
847 // #i17440# space between font height and numbers in text
848 if( bFontHtChanged
&& aParaText
.getLength() && !aPortionText
.isEmpty() )
850 sal_Unicode cLast
= aParaText
[ aParaText
.getLength() - 1 ];
851 sal_Unicode cFirst
= aPortionText
[0];
852 if( ('0' <= cLast
) && (cLast
<= '9') && ('0' <= cFirst
) && (cFirst
<= '9') )
855 aParaText
+= aPortionText
;
859 aSel
.nStartPos
= aSel
.nEndPos
;
862 aText
= ScGlobal::addToken( aText
, aParaText
, '\n' );
863 if( nParaHeight
== 0 )
864 nParaHeight
= aFontData
.mnHeight
* 20; // points -> twips
865 nHeight
+= nParaHeight
;
868 mrEE
.SetUpdateMode( bOldUpdateMode
);
870 if( !aText
.isEmpty() )
872 maHFString
+= "&" + OUString(cPortionCode
) + aText
;
873 mnTotalHeight
= ::std::max( mnTotalHeight
, nHeight
);
877 // URL conversion =============================================================
881 /** Encodes special parts of the URL, i.e. directory separators and volume names.
882 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
883 OUString
lclEncodeDosUrl(
884 XclBiff eBiff
, const OUString
& rUrl
, const OUString
& rBase
, const OUString
* pTableName
)
890 OUString aOldUrl
= rUrl
;
891 aBuf
.append(EXC_URLSTART_ENCODED
);
893 if ( aOldUrl
.getLength() > 2 && aOldUrl
.copy(0,2) == "\\\\" )
896 aBuf
.append(EXC_URL_DOSDRIVE
).append('@');
897 aOldUrl
= aOldUrl
.copy(2);
899 else if ( aOldUrl
.getLength() > 2 && aOldUrl
.copy(1,2) == ":\\" )
902 sal_Unicode cThisDrive
= rBase
.isEmpty() ? ' ' : rBase
[0];
903 sal_Unicode cDrive
= aOldUrl
[0];
904 if (cThisDrive
== cDrive
)
905 // This document and the referenced document are under the same drive.
906 aBuf
.append(EXC_URL_DRIVEROOT
);
908 aBuf
.append(EXC_URL_DOSDRIVE
).append(cDrive
);
909 aOldUrl
= aOldUrl
.copy(3);
913 // URL probably points to a document on a Unix-like file system
914 aBuf
.append(EXC_URL_DRIVEROOT
);
919 while((nPos
= aOldUrl
.indexOf('\\')) != -1)
921 if ( aOldUrl
.copy(0,2) == ".." )
922 // parent dir (NOTE: the MS-XLS spec doesn't mention this, and
923 // Excel seems confused by this token).
924 aBuf
.append(EXC_URL_PARENTDIR
);
926 aBuf
.append(aOldUrl
.copy(0,nPos
)).append(EXC_URL_SUBDIR
);
928 aOldUrl
= aOldUrl
.copy(nPos
+ 1);
932 if (pTableName
) // enclose file name in brackets if table name follows
933 aBuf
.append('[').append(aOldUrl
).append(']');
935 aBuf
.append(aOldUrl
);
937 else // empty URL -> self reference
942 aBuf
.append(pTableName
? EXC_URLSTART_SELFENCODED
: EXC_URLSTART_SELF
);
945 DBG_ASSERT( pTableName
, "lclEncodeDosUrl - sheet name required for BIFF8" );
946 aBuf
.append(EXC_URLSTART_SELF
);
955 aBuf
.append(*pTableName
);
957 // VirtualPath must be shorter than 255 chars ([MS-XLS].pdf 2.5.277)
958 // It's better to truncate, than generate invalid file that Excel cannot open.
959 if (aBuf
.getLength() > 255)
962 return aBuf
.makeStringAndClear();
967 OUString
XclExpUrlHelper::EncodeUrl( const XclExpRoot
& rRoot
, const OUString
& rAbsUrl
, const OUString
* pTableName
)
969 OUString aDosUrl
= INetURLObject(rAbsUrl
).getFSysPath(INetURLObject::FSYS_DOS
);
970 OUString aDosBase
= INetURLObject(rRoot
.GetBasePath()).getFSysPath(INetURLObject::FSYS_DOS
);
971 return lclEncodeDosUrl(rRoot
.GetBiff(), aDosUrl
, aDosBase
, pTableName
);
974 OUString
XclExpUrlHelper::EncodeDde( const OUString
& rApplic
, const OUString
& rTopic
)
977 aBuf
.append(rApplic
).append(EXC_DDE_DELIM
).append(rTopic
);
978 return aBuf
.makeStringAndClear();
981 // Cached Value Lists =========================================================
983 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix
& rMatrix
)
984 : mrMatrix( rMatrix
)
988 XclExpCachedMatrix::~XclExpCachedMatrix()
993 void XclExpCachedMatrix::GetDimensions( SCSIZE
& nCols
, SCSIZE
& nRows
) const
995 mrMatrix
.GetDimensions( nCols
, nRows
);
997 OSL_ENSURE( nCols
&& nRows
, "XclExpCachedMatrix::GetDimensions - empty matrix" );
998 OSL_ENSURE( nCols
<= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
1001 sal_Size
XclExpCachedMatrix::GetSize() const
1003 SCSIZE nCols
, nRows
;
1005 GetDimensions( nCols
, nRows
);
1007 /* The returned size may be wrong if the matrix contains strings. The only
1008 effect is that the export stream has to update a wrong record size which is
1009 faster than to iterate through all cached values and calculate their sizes. */
1010 return 3 + 9 * (nCols
* nRows
);
1013 void XclExpCachedMatrix::Save( XclExpStream
& rStrm
) const
1015 SCSIZE nCols
, nRows
;
1017 GetDimensions( nCols
, nRows
);
1019 if( rStrm
.GetRoot().GetBiff() <= EXC_BIFF5
)
1020 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1021 rStrm
<< static_cast< sal_uInt8
>( nCols
) << static_cast< sal_uInt16
>( nRows
);
1023 // in BIFF8: columns and rows decreaed by 1
1024 rStrm
<< static_cast< sal_uInt8
>( nCols
- 1 ) << static_cast< sal_uInt16
>( nRows
- 1 );
1026 for( SCSIZE nRow
= 0; nRow
< nRows
; ++nRow
)
1028 for( SCSIZE nCol
= 0; nCol
< nCols
; ++nCol
)
1030 ScMatrixValue nMatVal
= mrMatrix
.Get( nCol
, nRow
);
1032 if( SC_MATVAL_EMPTY
== nMatVal
.nType
)
1034 rStrm
.SetSliceSize( 9 );
1035 rStrm
<< EXC_CACHEDVAL_EMPTY
;
1036 rStrm
.WriteZeroBytes( 8 );
1038 else if( ScMatrix::IsNonValueType( nMatVal
.nType
) )
1040 XclExpString
aStr( nMatVal
.GetString().getString(), EXC_STR_DEFAULT
);
1041 rStrm
.SetSliceSize( 6 );
1042 rStrm
<< EXC_CACHEDVAL_STRING
<< aStr
;
1044 else if( SC_MATVAL_BOOLEAN
== nMatVal
.nType
)
1046 sal_Int8 nBool
= sal_Int8(nMatVal
.GetBoolean());
1047 rStrm
.SetSliceSize( 9 );
1048 rStrm
<< EXC_CACHEDVAL_BOOL
<< nBool
;
1049 rStrm
.WriteZeroBytes( 7 );
1051 else if( sal_uInt16 nScError
= nMatVal
.GetError() )
1053 sal_Int8
nError ( XclTools::GetXclErrorCode( nScError
) );
1054 rStrm
.SetSliceSize( 9 );
1055 rStrm
<< EXC_CACHEDVAL_ERROR
<< nError
;
1056 rStrm
.WriteZeroBytes( 7 );
1060 rStrm
.SetSliceSize( 9 );
1061 rStrm
<< EXC_CACHEDVAL_DOUBLE
<< nMatVal
.fVal
;
1067 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */