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 <sal/config.h>
22 #include <string_view>
24 #include <com/sun/star/i18n/XBreakIterator.hpp>
25 #include <com/sun/star/i18n/ScriptType.hpp>
26 #include <sfx2/objsh.hxx>
27 #include <vcl/font.hxx>
28 #include <tools/urlobj.hxx>
29 #include <svl/itemset.hxx>
30 #include <svtools/ctrltool.hxx>
31 #include <svx/svdotext.hxx>
32 #include <editeng/outlobj.hxx>
33 #include <scitems.hxx>
34 #include <editeng/fhgtitem.hxx>
35 #include <editeng/flstitem.hxx>
36 #include <editeng/colritem.hxx>
37 #include <editeng/eeitem.hxx>
38 #include <editeng/flditem.hxx>
39 #include <editeng/escapementitem.hxx>
40 #include <editeng/svxfont.hxx>
41 #include <editeng/editids.hrc>
43 #include <document.hxx>
44 #include <docpool.hxx>
45 #include <editutil.hxx>
46 #include <patattr.hxx>
47 #include <scmatrix.hxx>
48 #include <xestyle.hxx>
49 #include <fprogressbar.hxx>
50 #include <globstr.hrc>
51 #include <xltracer.hxx>
52 #include <xltools.hxx>
53 #include <xecontent.hxx>
55 #include <xehelper.hxx>
57 using ::com::sun::star::uno::Reference
;
58 using ::com::sun::star::i18n::XBreakIterator
;
60 // Export progress bar ========================================================
62 XclExpProgressBar::XclExpProgressBar( const XclExpRoot
& rRoot
) :
64 mxProgress( new ScfProgressBar( rRoot
.GetDocShell(), STR_SAVE_DOC
) ),
65 mpSubProgress( nullptr ),
66 mpSubRowCreate( nullptr ),
67 mpSubRowFinal( nullptr ),
68 mnSegRowFinal( SCF_INV_SEGMENT
),
73 XclExpProgressBar::~XclExpProgressBar()
77 void XclExpProgressBar::Initialize()
79 const ScDocument
& rDoc
= GetDoc();
80 const XclExpTabInfo
& rTabInfo
= GetTabInfo();
81 SCTAB nScTabCount
= rTabInfo
.GetScTabCount();
83 // *** segment: creation of ROW records *** -------------------------------
85 sal_Int32 nSegRowCreate
= mxProgress
->AddSegment( 2000 );
86 mpSubRowCreate
= &mxProgress
->GetSegmentProgressBar( nSegRowCreate
);
87 maSubSegRowCreate
.resize( nScTabCount
, SCF_INV_SEGMENT
);
89 for( SCTAB nScTab
= 0; nScTab
< nScTabCount
; ++nScTab
)
91 if( rTabInfo
.IsExportTab( nScTab
) )
95 rDoc
.GetTableArea( nScTab
, nLastUsedScCol
, nLastUsedScRow
);
96 std::size_t nSegSize
= static_cast< std::size_t >( nLastUsedScRow
+ 1 );
97 maSubSegRowCreate
[ nScTab
] = mpSubRowCreate
->AddSegment( nSegSize
);
101 // *** segment: writing all ROW records *** -------------------------------
103 mnSegRowFinal
= mxProgress
->AddSegment( 1000 );
104 // sub progress bar and segment are created later in ActivateFinalRowsSegment()
107 void XclExpProgressBar::IncRowRecordCount()
112 void XclExpProgressBar::ActivateCreateRowsSegment()
114 OSL_ENSURE( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
115 "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
116 sal_Int32 nSeg
= maSubSegRowCreate
[ GetCurrScTab() ];
117 OSL_ENSURE( nSeg
!= SCF_INV_SEGMENT
, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
118 if( nSeg
!= SCF_INV_SEGMENT
)
120 mpSubProgress
= mpSubRowCreate
;
121 mpSubProgress
->ActivateSegment( nSeg
);
124 mpSubProgress
= nullptr;
127 void XclExpProgressBar::ActivateFinalRowsSegment()
129 if( !mpSubRowFinal
&& (mnRowCount
> 0) )
131 mpSubRowFinal
= &mxProgress
->GetSegmentProgressBar( mnSegRowFinal
);
132 mpSubRowFinal
->AddSegment( mnRowCount
);
134 mpSubProgress
= mpSubRowFinal
;
136 mpSubProgress
->Activate();
139 void XclExpProgressBar::Progress()
141 if( mpSubProgress
&& !mpSubProgress
->IsFull() )
142 mpSubProgress
->Progress();
145 // Calc->Excel cell address/range conversion ==================================
149 /** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
150 void lclFillAddress( XclAddress
& rXclPos
, SCCOL nScCol
, SCROW nScRow
)
152 rXclPos
.mnCol
= static_cast< sal_uInt16
>( nScCol
);
153 rXclPos
.mnRow
= static_cast< sal_uInt32
>( nScRow
);
158 XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot
& rRoot
) :
159 XclAddressConverterBase( rRoot
.GetTracer(), rRoot
.GetXclMaxPos() )
163 // cell address ---------------------------------------------------------------
165 bool XclExpAddressConverter::CheckAddress( const ScAddress
& rScPos
, bool bWarn
)
167 // ScAddress::operator<=() doesn't do what we want here
168 bool bValidCol
= (0 <= rScPos
.Col()) && (rScPos
.Col() <= maMaxPos
.Col());
169 bool bValidRow
= (0 <= rScPos
.Row()) && (rScPos
.Row() <= maMaxPos
.Row());
170 bool bValidTab
= (0 <= rScPos
.Tab()) && (rScPos
.Tab() <= maMaxPos
.Tab());
172 bool bValid
= bValidCol
&& bValidRow
&& bValidTab
;
175 mbColTrunc
|= !bValidCol
;
176 mbRowTrunc
|= !bValidRow
;
178 if( !bValid
&& bWarn
)
180 mbTabTrunc
|= (rScPos
.Tab() > maMaxPos
.Tab()); // do not warn for deleted refs
181 mrTracer
.TraceInvalidAddress( rScPos
, maMaxPos
);
186 bool XclExpAddressConverter::ConvertAddress( XclAddress
& rXclPos
,
187 const ScAddress
& rScPos
, bool bWarn
)
189 bool bValid
= CheckAddress( rScPos
, bWarn
);
191 lclFillAddress( rXclPos
, rScPos
.Col(), rScPos
.Row() );
195 XclAddress
XclExpAddressConverter::CreateValidAddress( const ScAddress
& rScPos
, bool bWarn
)
197 XclAddress
aXclPos( ScAddress::UNINITIALIZED
);
198 if( !ConvertAddress( aXclPos
, rScPos
, bWarn
) )
199 lclFillAddress( aXclPos
, ::std::min( rScPos
.Col(), maMaxPos
.Col() ), ::std::min( rScPos
.Row(), maMaxPos
.Row() ) );
203 // cell range -----------------------------------------------------------------
205 bool XclExpAddressConverter::CheckRange( const ScRange
& rScRange
, bool bWarn
)
207 return CheckAddress( rScRange
.aStart
, bWarn
) && CheckAddress( rScRange
.aEnd
, bWarn
);
210 bool XclExpAddressConverter::ValidateRange( ScRange
& rScRange
, bool bWarn
)
212 rScRange
.PutInOrder();
214 // check start position
215 bool bValidStart
= CheckAddress( rScRange
.aStart
, bWarn
);
218 // check & correct end position
219 ScAddress
& rScEnd
= rScRange
.aEnd
;
220 if( !CheckAddress( rScEnd
, bWarn
) )
222 rScEnd
.SetCol( ::std::min( rScEnd
.Col(), maMaxPos
.Col() ) );
223 rScEnd
.SetRow( ::std::min( rScEnd
.Row(), maMaxPos
.Row() ) );
224 rScEnd
.SetTab( ::std::min( rScEnd
.Tab(), maMaxPos
.Tab() ) );
231 bool XclExpAddressConverter::ConvertRange( XclRange
& rXclRange
,
232 const ScRange
& rScRange
, bool bWarn
)
234 // check start position
235 bool bValidStart
= CheckAddress( rScRange
.aStart
, bWarn
);
238 lclFillAddress( rXclRange
.maFirst
, rScRange
.aStart
.Col(), rScRange
.aStart
.Row() );
240 // check & correct end position
241 SCCOL nScCol2
= rScRange
.aEnd
.Col();
242 SCROW nScRow2
= rScRange
.aEnd
.Row();
243 if( !CheckAddress( rScRange
.aEnd
, bWarn
) )
245 nScCol2
= ::std::min( nScCol2
, maMaxPos
.Col() );
246 nScRow2
= ::std::min( nScRow2
, maMaxPos
.Row() );
248 lclFillAddress( rXclRange
.maLast
, nScCol2
, nScRow2
);
253 // cell range list ------------------------------------------------------------
255 void XclExpAddressConverter::ValidateRangeList( ScRangeList
& rScRanges
, bool bWarn
)
257 for ( size_t nRange
= rScRanges
.size(); nRange
> 0; )
259 ScRange
& rScRange
= rScRanges
[ --nRange
];
260 if( !CheckRange( rScRange
, bWarn
) )
261 rScRanges
.Remove(nRange
);
265 void XclExpAddressConverter::ConvertRangeList( XclRangeList
& rXclRanges
,
266 const ScRangeList
& rScRanges
, bool bWarn
)
269 for( size_t nPos
= 0, nCount
= rScRanges
.size(); nPos
< nCount
; ++nPos
)
271 const ScRange
& rScRange
= rScRanges
[ nPos
];
272 XclRange
aXclRange( ScAddress::UNINITIALIZED
);
273 if( ConvertRange( aXclRange
, rScRange
, bWarn
) )
274 rXclRanges
.push_back( aXclRange
);
278 // EditEngine->String conversion ==============================================
282 OUString
lclGetUrlRepresentation( const SvxURLField
& rUrlField
)
284 const OUString
& aRepr
= rUrlField
.GetRepresentation();
285 // no representation -> use URL
286 return aRepr
.isEmpty() ? rUrlField
.GetURL() : aRepr
;
291 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot
& rRoot
, const ScAddress
& rScPos
) :
294 mbMultipleUrls( false )
298 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
302 OUString
XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField
& rUrlField
)
306 if( GetBiff() == EXC_BIFF8
) // no HLINK records in BIFF2-BIFF7
308 // there was/is already a HLINK record
309 mbMultipleUrls
= static_cast< bool >(mxLinkRec
);
311 mxLinkRec
= new XclExpHyperlink( GetRoot(), rUrlField
, maScPos
);
313 if( const OUString
* pRepr
= mxLinkRec
->GetRepr() )
316 // add URL to note text
317 maUrlList
= ScGlobal::addToken( maUrlList
, rUrlField
.GetURL(), '\n' );
320 // no hyperlink representation from Excel HLINK record -> use it from text field
321 return aUrlRepr
.isEmpty() ? lclGetUrlRepresentation(rUrlField
) : aUrlRepr
;
324 bool XclExpHyperlinkHelper::HasLinkRecord() const
326 return !mbMultipleUrls
&& mxLinkRec
;
329 XclExpHyperlinkHelper::XclExpHyperlinkRef
XclExpHyperlinkHelper::GetLinkRecord() const
331 if( HasLinkRecord() )
333 return XclExpHyperlinkRef();
338 /** Creates a new formatted string from the passed unformatted string.
340 Creates a Unicode string or a byte string, depending on the current BIFF
341 version contained in the passed XclExpRoot object. May create a formatted
342 string object, if the text contains different script types.
345 Cell attributes used for font formatting.
347 Modifiers for string export.
349 The maximum number of characters to store in this string.
351 The new string object.
353 XclExpStringRef
lclCreateFormattedString(
354 const XclExpRoot
& rRoot
, const OUString
& rText
, const ScPatternAttr
* pCellAttr
,
355 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
357 /* Create an empty Excel string object with correctly initialized BIFF mode,
358 because this function only uses Append() functions that require this. */
359 XclExpStringRef xString
= XclExpStringHelper::CreateString( rRoot
, OUString(), nFlags
, nMaxLen
);
361 // script type handling
362 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
363 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
364 // #i63255# get script type for leading weak characters
365 sal_Int16 nLastScript
= XclExpStringHelper::GetLeadingScriptType( rRoot
, rText
);
367 // font buffer and cell item set
368 XclExpFontBuffer
& rFontBuffer
= rRoot
.GetFontBuffer();
369 const SfxItemSet
& rItemSet
= pCellAttr
? pCellAttr
->GetItemSet() : rRoot
.GetDoc().GetDefPattern()->GetItemSet();
371 // process all script portions
372 sal_Int32 nPortionPos
= 0;
373 sal_Int32 nTextLen
= rText
.getLength();
374 while( nPortionPos
< nTextLen
)
376 // get script type and end position of next script portion
377 sal_Int16 nScript
= xBreakIt
->getScriptType( rText
, nPortionPos
);
378 sal_Int32 nPortionEnd
= xBreakIt
->endOfScript( rText
, nPortionPos
, nScript
);
380 // reuse previous script for following weak portions
381 if( nScript
== ApiScriptType::WEAK
)
382 nScript
= nLastScript
;
384 // construct font from current text portion
385 SvxFont
aFont( XclExpFontHelper::GetFontFromItemSet( rRoot
, rItemSet
, nScript
) );
387 // Excel start position of this portion
388 sal_Int32 nXclPortionStart
= xString
->Len();
389 // add portion text to Excel string
390 XclExpStringHelper::AppendString( *xString
, rRoot
, rText
.copy( nPortionPos
, nPortionEnd
- nPortionPos
) );
391 if( nXclPortionStart
< xString
->Len() )
393 // insert font into buffer
394 sal_uInt16 nFontIdx
= rFontBuffer
.Insert( aFont
, EXC_COLOR_CELLTEXT
);
395 // insert font index into format run vector
396 xString
->AppendFormat( nXclPortionStart
, nFontIdx
);
399 // go to next script portion
400 nLastScript
= nScript
;
401 nPortionPos
= nPortionEnd
;
407 /** Creates a new formatted string from an edit engine text object.
409 Creates a Unicode string or a byte string, depending on the current BIFF
410 version contained in the passed XclExpRoot object.
413 The edit engine in use. The text object must already be set.
415 Modifiers for string export.
417 The maximum number of characters to store in this string.
419 The new string object.
421 XclExpStringRef
lclCreateFormattedString(
422 const XclExpRoot
& rRoot
, EditEngine
& rEE
, XclExpHyperlinkHelper
* pLinkHelper
,
423 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
425 /* Create an empty Excel string object with correctly initialized BIFF mode,
426 because this function only uses Append() functions that require this. */
427 XclExpStringRef xString
= XclExpStringHelper::CreateString( rRoot
, OUString(), nFlags
, nMaxLen
);
429 // font buffer and helper item set for edit engine -> Calc item conversion
430 XclExpFontBuffer
& rFontBuffer
= rRoot
.GetFontBuffer();
431 SfxItemSetFixed
<ATTR_PATTERN_START
, ATTR_PATTERN_END
> aItemSet( *rRoot
.GetDoc().GetPool() );
433 // script type handling
434 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
435 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
436 // #i63255# get script type for leading weak characters
437 sal_Int16 nLastScript
= XclExpStringHelper::GetLeadingScriptType( rRoot
, rEE
.GetText() );
439 // process all paragraphs
440 sal_Int32 nParaCount
= rEE
.GetParagraphCount();
441 for( sal_Int32 nPara
= 0; nPara
< nParaCount
; ++nPara
)
443 ESelection
aSel( nPara
, 0 );
444 OUString
aParaText( rEE
.GetText( nPara
) );
446 std::vector
<sal_Int32
> aPosList
;
447 rEE
.GetPortions( nPara
, aPosList
);
449 // process all portions in the paragraph
450 for( const auto& rPos
: aPosList
)
453 OUString aXclPortionText
= aParaText
.copy( aSel
.nStartPos
, aSel
.nEndPos
- aSel
.nStartPos
);
455 aItemSet
.ClearItem();
456 SfxItemSet
aEditSet( rEE
.GetAttribs( aSel
) );
457 ScPatternAttr::GetFromEditItemSet( aItemSet
, aEditSet
);
459 // get escapement value
460 short nEsc
= aEditSet
.Get( EE_CHAR_ESCAPEMENT
).GetEsc();
462 // process text fields
463 bool bIsHyperlink
= false;
464 if( aSel
.nStartPos
+ 1 == aSel
.nEndPos
)
466 // test if the character is a text field
467 const SfxPoolItem
* pItem
;
468 if( aEditSet
.GetItemState( EE_FEATURE_FIELD
, false, &pItem
) == SfxItemState::SET
)
470 const SvxFieldData
* pField
= static_cast< const SvxFieldItem
* >( pItem
)->GetField();
471 if( const SvxURLField
* pUrlField
= dynamic_cast<const SvxURLField
*>( pField
) )
473 // convert URL field to string representation
474 aXclPortionText
= pLinkHelper
?
475 pLinkHelper
->ProcessUrlField( *pUrlField
) :
476 lclGetUrlRepresentation( *pUrlField
);
481 OSL_FAIL( "lclCreateFormattedString - unknown text field" );
482 aXclPortionText
.clear();
487 // Excel start position of this portion
488 sal_Int32 nXclPortionStart
= xString
->Len();
489 // add portion text to Excel string
490 XclExpStringHelper::AppendString( *xString
, rRoot
, aXclPortionText
);
491 if( (nXclPortionStart
< xString
->Len()) || (aParaText
.isEmpty()) )
493 /* Construct font from current edit engine text portion. Edit engine
494 creates different portions for different script types, no need to loop. */
495 sal_Int16 nScript
= xBreakIt
->getScriptType( aXclPortionText
, 0 );
496 if( nScript
== ApiScriptType::WEAK
)
497 nScript
= nLastScript
;
498 SvxFont
aFont( XclExpFontHelper::GetFontFromItemSet( rRoot
, aItemSet
, nScript
) );
499 nLastScript
= nScript
;
502 aFont
.SetEscapement( nEsc
);
503 // modify automatic font color for hyperlinks
504 if( bIsHyperlink
&& aItemSet
.Get( ATTR_FONT_COLOR
).GetValue() == COL_AUTO
)
505 aFont
.SetColor( COL_LIGHTBLUE
);
507 // insert font into buffer
508 sal_uInt16 nFontIdx
= rFontBuffer
.Insert( aFont
, EXC_COLOR_CELLTEXT
);
509 // insert font index into format run vector
510 xString
->AppendFormat( nXclPortionStart
, nFontIdx
);
513 aSel
.nStartPos
= aSel
.nEndPos
;
516 // add trailing newline (important for correct character index calculation)
517 if( nPara
+ 1 < nParaCount
)
518 XclExpStringHelper::AppendChar( *xString
, rRoot
, '\n' );
526 XclExpStringRef
XclExpStringHelper::CreateString(
527 const XclExpRoot
& rRoot
, const OUString
& rString
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
529 XclExpStringRef xString
= std::make_shared
<XclExpString
>();
530 if( rRoot
.GetBiff() == EXC_BIFF8
)
531 xString
->Assign( rString
, nFlags
, nMaxLen
);
533 xString
->AssignByte( rString
, rRoot
.GetTextEncoding(), nFlags
, nMaxLen
);
537 XclExpStringRef
XclExpStringHelper::CreateString(
538 const XclExpRoot
& rRoot
, sal_Unicode cChar
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
540 XclExpStringRef xString
= CreateString( rRoot
, OUString(), nFlags
, nMaxLen
);
541 AppendChar( *xString
, rRoot
, cChar
);
545 void XclExpStringHelper::AppendString( XclExpString
& rXclString
, const XclExpRoot
& rRoot
, const OUString
& rString
)
547 if( rRoot
.GetBiff() == EXC_BIFF8
)
548 rXclString
.Append( rString
);
550 rXclString
.AppendByte( rString
, rRoot
.GetTextEncoding() );
553 void XclExpStringHelper::AppendChar( XclExpString
& rXclString
, const XclExpRoot
& rRoot
, sal_Unicode cChar
)
555 if( rRoot
.GetBiff() == EXC_BIFF8
)
556 rXclString
.Append( OUString(cChar
) );
558 rXclString
.AppendByte( cChar
, rRoot
.GetTextEncoding() );
561 XclExpStringRef
XclExpStringHelper::CreateCellString(
562 const XclExpRoot
& rRoot
, const OUString
& rString
, const ScPatternAttr
* pCellAttr
,
563 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
565 return lclCreateFormattedString(rRoot
, rString
, pCellAttr
, nFlags
, nMaxLen
);
568 XclExpStringRef
XclExpStringHelper::CreateCellString(
569 const XclExpRoot
& rRoot
, const EditTextObject
& rEditText
, const ScPatternAttr
* pCellAttr
,
570 XclExpHyperlinkHelper
& rLinkHelper
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
572 XclExpStringRef xString
;
575 ScEditEngineDefaulter
& rEE
= rRoot
.GetEditEngine();
576 bool bOldUpdateMode
= rEE
.SetUpdateLayout( true );
579 const SfxItemSet
& rItemSet
= pCellAttr
? pCellAttr
->GetItemSet() : rRoot
.GetDoc().GetDefPattern()->GetItemSet();
580 auto pEEItemSet
= std::make_unique
<SfxItemSet
>( rEE
.GetEmptyItemSet() );
581 ScPatternAttr::FillToEditItemSet( *pEEItemSet
, rItemSet
);
582 rEE
.SetDefaults( std::move(pEEItemSet
) ); // edit engine takes ownership
585 rEE
.SetTextCurrentDefaults(rEditText
);
586 xString
= lclCreateFormattedString( rRoot
, rEE
, &rLinkHelper
, nFlags
, nMaxLen
);
587 rEE
.SetUpdateLayout( bOldUpdateMode
);
592 XclExpStringRef
XclExpStringHelper::CreateString(
593 const XclExpRoot
& rRoot
, const SdrTextObj
& rTextObj
,
596 XclExpStringRef xString
;
597 if( const OutlinerParaObject
* pParaObj
= rTextObj
.GetOutlinerParaObject() )
599 EditEngine
& rEE
= rRoot
.GetDrawEditEngine();
600 bool bOldUpdateMode
= rEE
.SetUpdateLayout( true );
602 rEE
.SetText( pParaObj
->GetTextObject() );
603 xString
= lclCreateFormattedString( rRoot
, rEE
, nullptr, nFlags
, EXC_STR_MAXLEN
);
604 rEE
.SetUpdateLayout( bOldUpdateMode
);
605 // limit formats - TODO: BIFF dependent
606 if( !xString
->IsEmpty() )
608 xString
->LimitFormatCount( EXC_MAXRECSIZE_BIFF8
/ 8 - 1 );
609 xString
->AppendTrailingFormat( EXC_FONT_APP
);
614 OSL_FAIL( "XclExpStringHelper::CreateString - textbox without para object" );
615 // create BIFF dependent empty Excel string
616 xString
= CreateString( rRoot
, OUString(), nFlags
);
621 XclExpStringRef
XclExpStringHelper::CreateString(
622 const XclExpRoot
& rRoot
, const EditTextObject
& rEditObj
,
625 XclExpStringRef xString
;
626 EditEngine
& rEE
= rRoot
.GetDrawEditEngine();
627 bool bOldUpdateMode
= rEE
.SetUpdateLayout( true );
628 rEE
.SetText( rEditObj
);
629 xString
= lclCreateFormattedString( rRoot
, rEE
, nullptr, nFlags
, EXC_STR_MAXLEN
);
630 rEE
.SetUpdateLayout( 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 sal_Int32 nStrPos
= 0;
645 sal_Int32 nStrLen
= rString
.getLength();
646 sal_Int16 nScript
= ApiScriptType::WEAK
;
647 while( (nStrPos
< nStrLen
) && (nScript
== ApiScriptType::WEAK
) )
649 nScript
= xBreakIt
->getScriptType( rString
, nStrPos
);
650 nStrPos
= xBreakIt
->endOfScript( rString
, nStrPos
, nScript
);
652 return (nScript
== ApiScriptType::WEAK
) ? rRoot
.GetDefApiScript() : nScript
;
655 // Header/footer conversion ===================================================
657 XclExpHFConverter::XclExpHFConverter( const XclExpRoot
& rRoot
) :
659 mrEE( rRoot
.GetHFEditEngine() ),
664 void XclExpHFConverter::GenerateString(
665 const EditTextObject
* pLeftObj
,
666 const EditTextObject
* pCenterObj
,
667 const EditTextObject
* pRightObj
)
671 AppendPortion( pLeftObj
, 'L' );
672 AppendPortion( pCenterObj
, 'C' );
673 AppendPortion( pRightObj
, 'R' );
676 void XclExpHFConverter::AppendPortion( const EditTextObject
* pTextObj
, sal_Unicode cPortionCode
)
678 if( !pTextObj
) return;
681 sal_Int32 nHeight
= 0;
682 SfxItemSetFixed
<ATTR_PATTERN_START
, ATTR_PATTERN_END
> aItemSet( *GetDoc().GetPool() );
685 bool bOldUpdateMode
= mrEE
.SetUpdateLayout( true );
686 mrEE
.SetText( *pTextObj
);
689 XclFontData aFontData
, aNewData
;
690 if( const XclExpFont
* pFirstFont
= GetFontBuffer().GetFont( EXC_FONT_APP
) )
692 aFontData
= pFirstFont
->GetFontData();
693 aFontData
.mnHeight
= (aFontData
.mnHeight
+ 10) / 20; // using pt here, not twips
696 aFontData
.mnHeight
= 10;
698 const FontList
* pFontList
= nullptr;
699 if( SfxObjectShell
* pDocShell
= GetDocShell() )
701 if( const SvxFontListItem
* pInfoItem
= static_cast< const SvxFontListItem
* >(
702 pDocShell
->GetItem( SID_ATTR_CHAR_FONTLIST
) ) )
703 pFontList
= pInfoItem
->GetFontList();
706 sal_Int32 nParaCount
= mrEE
.GetParagraphCount();
707 for( sal_Int32 nPara
= 0; nPara
< nParaCount
; ++nPara
)
709 ESelection
aSel( nPara
, 0 );
710 OUStringBuffer aParaText
;
711 sal_Int32 nParaHeight
= 0;
712 std::vector
<sal_Int32
> aPosList
;
713 mrEE
.GetPortions( nPara
, aPosList
);
715 for( const auto& rPos
: aPosList
)
718 if( aSel
.nStartPos
< aSel
.nEndPos
)
721 // --- font attributes ---
724 aItemSet
.ClearItem();
725 SfxItemSet
aEditSet( mrEE
.GetAttribs( aSel
) );
726 ScPatternAttr::GetFromEditItemSet( aItemSet
, aEditSet
);
727 ScPatternAttr::GetFont( aFont
, aItemSet
, SC_AUTOCOL_RAW
);
729 // font name and style
730 aNewData
.maName
= XclTools::GetXclFontName( aFont
.GetFamilyName() );
731 aNewData
.mnWeight
= (aFont
.GetWeight() > WEIGHT_NORMAL
) ? EXC_FONTWGHT_BOLD
: EXC_FONTWGHT_NORMAL
;
732 aNewData
.mbItalic
= (aFont
.GetItalic() != ITALIC_NONE
);
733 bool bNewFont
= (aFontData
.maName
!= aNewData
.maName
);
734 bool bNewStyle
= (aFontData
.mnWeight
!= aNewData
.mnWeight
) ||
735 (aFontData
.mbItalic
!= aNewData
.mbItalic
);
736 if( bNewFont
|| (bNewStyle
&& pFontList
) )
738 aParaText
.append("&\"" + aNewData
.maName
);
741 FontMetric
aFontMetric( pFontList
->Get(
743 (aNewData
.mnWeight
> EXC_FONTWGHT_NORMAL
) ? WEIGHT_BOLD
: WEIGHT_NORMAL
,
744 aNewData
.mbItalic
? ITALIC_NORMAL
: ITALIC_NONE
) );
745 aNewData
.maStyle
= pFontList
->GetStyleName( aFontMetric
);
746 if( !aNewData
.maStyle
.isEmpty() )
747 aParaText
.append("," + aNewData
.maStyle
);
749 aParaText
.append("\"");
753 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
754 // -> get it directly from edit engine item set
755 aNewData
.mnHeight
= ulimit_cast
< sal_uInt16
>( aEditSet
.Get( EE_CHAR_FONTHEIGHT
).GetHeight() );
756 aNewData
.mnHeight
= (aNewData
.mnHeight
+ 10) / 20;
757 bool bFontHtChanged
= (aFontData
.mnHeight
!= aNewData
.mnHeight
);
759 aParaText
.append("&" + OUString::number(aNewData
.mnHeight
));
760 // update maximum paragraph height, convert to twips
761 nParaHeight
= ::std::max
< sal_Int32
>( nParaHeight
, aNewData
.mnHeight
* 20 );
764 aNewData
.mnUnderline
= EXC_FONTUNDERL_NONE
;
765 switch( aFont
.GetUnderline() )
767 case LINESTYLE_NONE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_NONE
; break;
768 case LINESTYLE_SINGLE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_SINGLE
; break;
769 case LINESTYLE_DOUBLE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_DOUBLE
; break;
770 default: aNewData
.mnUnderline
= EXC_FONTUNDERL_SINGLE
;
772 if( aFontData
.mnUnderline
!= aNewData
.mnUnderline
)
774 sal_uInt8 nTmpUnderl
= (aNewData
.mnUnderline
== EXC_FONTUNDERL_NONE
) ?
775 aFontData
.mnUnderline
: aNewData
.mnUnderline
;
776 (nTmpUnderl
== EXC_FONTUNDERL_SINGLE
)? aParaText
.append("&U") : aParaText
.append("&E");
780 aNewData
.maColor
= aFont
.GetColor();
781 if ( !aFontData
.maColor
.IsRGBEqual( aNewData
.maColor
) )
783 aParaText
.append("&K" + aNewData
.maColor
.AsRGBHexString());
787 aNewData
.mbStrikeout
= (aFont
.GetStrikeout() != STRIKEOUT_NONE
);
788 if( aFontData
.mbStrikeout
!= aNewData
.mbStrikeout
)
789 aParaText
.append("&S");
792 const SvxEscapementItem
& rEscapeItem
= aEditSet
.Get( EE_CHAR_ESCAPEMENT
);
793 aNewData
.SetScEscapement( rEscapeItem
.GetEsc() );
794 if( aFontData
.mnEscapem
!= aNewData
.mnEscapem
)
796 switch(aNewData
.mnEscapem
)
798 // close the previous super/sub script.
799 case EXC_FONTESC_NONE
: (aFontData
.mnEscapem
== EXC_FONTESC_SUPER
) ? aParaText
.append("&X") : aParaText
.append("&Y"); break;
800 case EXC_FONTESC_SUPER
: aParaText
.append("&X"); break;
801 case EXC_FONTESC_SUB
: aParaText
.append("&Y"); break;
806 aFontData
= aNewData
;
808 // --- text content or text fields ---
810 const SfxPoolItem
* pItem
;
811 if( (aSel
.nStartPos
+ 1 == aSel
.nEndPos
) && // fields are single characters
812 (aEditSet
.GetItemState( EE_FEATURE_FIELD
, false, &pItem
) == SfxItemState::SET
) )
814 if( const SvxFieldData
* pFieldData
= static_cast< const SvxFieldItem
* >( pItem
)->GetField() )
816 if( dynamic_cast<const SvxPageField
*>( pFieldData
) != nullptr )
817 aParaText
.append("&P");
818 else if( dynamic_cast<const SvxPagesField
*>( pFieldData
) != nullptr )
819 aParaText
.append("&N");
820 else if( dynamic_cast<const SvxDateField
*>( pFieldData
) != nullptr )
821 aParaText
.append("&D");
822 else if( dynamic_cast<const SvxTimeField
*>( pFieldData
) != nullptr || dynamic_cast<const SvxExtTimeField
*>( pFieldData
) != nullptr )
823 aParaText
.append("&T");
824 else if( dynamic_cast<const SvxTableField
*>( pFieldData
) != nullptr )
825 aParaText
.append("&A");
826 else if( dynamic_cast<const SvxFileField
*>( pFieldData
) != nullptr ) // title -> file name
827 aParaText
.append("&F");
828 else if( const SvxExtFileField
* pFileField
= dynamic_cast<const SvxExtFileField
*>( pFieldData
) )
830 switch( pFileField
->GetFormat() )
832 case SvxFileFormat::NameAndExt
:
833 case SvxFileFormat::NameOnly
:
834 aParaText
.append("&F");
836 case SvxFileFormat::PathOnly
:
837 aParaText
.append("&Z");
839 case SvxFileFormat::PathFull
:
840 aParaText
.append("&Z&F");
843 OSL_FAIL( "XclExpHFConverter::AppendPortion - unknown file field" );
850 OUString
aPortionText( mrEE
.GetText( aSel
) );
851 aPortionText
= aPortionText
.replaceAll( "&", "&&" );
852 // #i17440# space between font height and numbers in text
853 if( bFontHtChanged
&& aParaText
.getLength() && !aPortionText
.isEmpty() )
855 sal_Unicode cLast
= aParaText
[ aParaText
.getLength() - 1 ];
856 sal_Unicode cFirst
= aPortionText
[0];
857 if( ('0' <= cLast
) && (cLast
<= '9') && ('0' <= cFirst
) && (cFirst
<= '9') )
858 aParaText
.append(" ");
860 aParaText
.append(aPortionText
);
864 aSel
.nStartPos
= aSel
.nEndPos
;
867 aText
= ScGlobal::addToken( aText
, aParaText
.makeStringAndClear(), '\n' );
868 if( nParaHeight
== 0 )
869 nParaHeight
= aFontData
.mnHeight
* 20; // points -> twips
870 nHeight
+= nParaHeight
;
873 mrEE
.SetUpdateLayout( bOldUpdateMode
);
875 if( !aText
.isEmpty() )
877 maHFString
+= "&" + OUStringChar(cPortionCode
) + aText
;
878 mnTotalHeight
= ::std::max( mnTotalHeight
, nHeight
);
882 // URL conversion =============================================================
886 /** Encodes special parts of the URL, i.e. directory separators and volume names.
887 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
888 OUString
lclEncodeDosUrl(
889 XclBiff eBiff
, const OUString
& rUrl
, std::u16string_view rBase
, const OUString
* pTableName
)
895 OUString aOldUrl
= rUrl
;
896 aBuf
.append(EXC_URLSTART_ENCODED
);
898 if ( aOldUrl
.getLength() > 2 && aOldUrl
.startsWith("\\\\") )
901 aBuf
.append(EXC_URL_DOSDRIVE
).append('@');
902 aOldUrl
= aOldUrl
.copy(2);
904 else if ( aOldUrl
.getLength() > 2 && aOldUrl
.match(":\\", 1) )
907 sal_Unicode cThisDrive
= rBase
.empty() ? ' ' : rBase
[0];
908 sal_Unicode cDrive
= aOldUrl
[0];
909 if (cThisDrive
== cDrive
)
910 // This document and the referenced document are under the same drive.
911 aBuf
.append(EXC_URL_DRIVEROOT
);
913 aBuf
.append(EXC_URL_DOSDRIVE
).append(cDrive
);
914 aOldUrl
= aOldUrl
.copy(3);
918 // URL probably points to a document on a Unix-like file system
919 aBuf
.append(EXC_URL_DRIVEROOT
);
924 while((nPos
= aOldUrl
.indexOf('\\')) != -1)
926 if ( aOldUrl
.startsWith("..") )
927 // parent dir (NOTE: the MS-XLS spec doesn't mention this, and
928 // Excel seems confused by this token).
929 aBuf
.append(EXC_URL_PARENTDIR
);
931 aBuf
.append(aOldUrl
.subView(0,nPos
)).append(EXC_URL_SUBDIR
);
933 aOldUrl
= aOldUrl
.copy(nPos
+ 1);
937 if (pTableName
) // enclose file name in brackets if table name follows
938 aBuf
.append('[').append(aOldUrl
).append(']');
940 aBuf
.append(aOldUrl
);
942 else // empty URL -> self reference
947 aBuf
.append(pTableName
? EXC_URLSTART_SELFENCODED
: EXC_URLSTART_SELF
);
950 DBG_ASSERT( pTableName
, "lclEncodeDosUrl - sheet name required for BIFF8" );
951 aBuf
.append(EXC_URLSTART_SELF
);
960 aBuf
.append(*pTableName
);
962 // VirtualPath must be shorter than 255 chars ([MS-XLS].pdf 2.5.277)
963 // It's better to truncate, than generate invalid file that Excel cannot open.
964 if (aBuf
.getLength() > 255)
967 return aBuf
.makeStringAndClear();
972 OUString
XclExpUrlHelper::EncodeUrl( const XclExpRoot
& rRoot
, const OUString
& rAbsUrl
, const OUString
* pTableName
)
974 OUString aDosUrl
= INetURLObject(rAbsUrl
).getFSysPath(FSysStyle::Dos
);
975 OUString aDosBase
= INetURLObject(rRoot
.GetBasePath()).getFSysPath(FSysStyle::Dos
);
976 return lclEncodeDosUrl(rRoot
.GetBiff(), aDosUrl
, aDosBase
, pTableName
);
979 OUString
XclExpUrlHelper::EncodeDde( std::u16string_view rApplic
, std::u16string_view rTopic
)
981 return rApplic
+ OUStringChar(EXC_DDE_DELIM
) + rTopic
;
984 // Cached Value Lists =========================================================
986 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix
& rMatrix
)
987 : mrMatrix( rMatrix
)
991 XclExpCachedMatrix::~XclExpCachedMatrix()
996 void XclExpCachedMatrix::GetDimensions( SCSIZE
& nCols
, SCSIZE
& nRows
) const
998 mrMatrix
.GetDimensions( nCols
, nRows
);
1000 OSL_ENSURE( nCols
&& nRows
, "XclExpCachedMatrix::GetDimensions - empty matrix" );
1001 OSL_ENSURE( nCols
<= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
1004 std::size_t XclExpCachedMatrix::GetSize() const
1006 SCSIZE nCols
, nRows
;
1008 GetDimensions( nCols
, nRows
);
1010 /* The returned size may be wrong if the matrix contains strings. The only
1011 effect is that the export stream has to update a wrong record size which is
1012 faster than to iterate through all cached values and calculate their sizes. */
1013 return 3 + 9 * (nCols
* nRows
);
1016 void XclExpCachedMatrix::Save( XclExpStream
& rStrm
) const
1018 SCSIZE nCols
, nRows
;
1020 GetDimensions( nCols
, nRows
);
1022 if( rStrm
.GetRoot().GetBiff() <= EXC_BIFF5
)
1023 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1024 rStrm
<< static_cast< sal_uInt8
>( nCols
) << static_cast< sal_uInt16
>( nRows
);
1026 // in BIFF8: columns and rows decreased by 1
1027 rStrm
<< static_cast< sal_uInt8
>( nCols
- 1 ) << static_cast< sal_uInt16
>( nRows
- 1 );
1029 for( SCSIZE nRow
= 0; nRow
< nRows
; ++nRow
)
1031 for( SCSIZE nCol
= 0; nCol
< nCols
; ++nCol
)
1033 ScMatrixValue nMatVal
= mrMatrix
.Get( nCol
, nRow
);
1035 FormulaError nScError
;
1036 if( ScMatValType::Empty
== nMatVal
.nType
)
1038 rStrm
.SetSliceSize( 9 );
1039 rStrm
<< EXC_CACHEDVAL_EMPTY
;
1040 rStrm
.WriteZeroBytes( 8 );
1042 else if( ScMatrix::IsNonValueType( nMatVal
.nType
) )
1044 XclExpString
aStr( nMatVal
.GetString().getString(), XclStrFlags::NONE
);
1045 rStrm
.SetSliceSize( 6 );
1046 rStrm
<< EXC_CACHEDVAL_STRING
<< aStr
;
1048 else if( ScMatValType::Boolean
== nMatVal
.nType
)
1050 sal_Int8 nBool
= sal_Int8(nMatVal
.GetBoolean());
1051 rStrm
.SetSliceSize( 9 );
1052 rStrm
<< EXC_CACHEDVAL_BOOL
<< nBool
;
1053 rStrm
.WriteZeroBytes( 7 );
1055 else if( (nScError
= nMatVal
.GetError()) != FormulaError::NONE
)
1057 sal_Int8
nError ( XclTools::GetXclErrorCode( nScError
) );
1058 rStrm
.SetSliceSize( 9 );
1059 rStrm
<< EXC_CACHEDVAL_ERROR
<< nError
;
1060 rStrm
.WriteZeroBytes( 7 );
1064 rStrm
.SetSliceSize( 9 );
1065 rStrm
<< EXC_CACHEDVAL_DOUBLE
<< nMatVal
.fVal
;
1071 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */