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 "richstring.hxx"
22 #include <com/sun/star/beans/XPropertySet.hpp>
23 #include <com/sun/star/text/XText.hpp>
24 #include <rtl/ustrbuf.hxx>
25 #include <editeng/editobj.hxx>
26 #include <oox/helper/attributelist.hxx>
27 #include <oox/helper/propertyset.hxx>
28 #include <oox/token/tokens.hxx>
29 #include "biffinputstream.hxx"
30 #include "editutil.hxx"
32 #include <vcl/svapp.hxx>
37 using namespace ::com::sun::star::text
;
38 using namespace ::com::sun::star::uno
;
42 const sal_uInt8 BIFF12_STRINGFLAG_FONTS
= 0x01;
43 const sal_uInt8 BIFF12_STRINGFLAG_PHONETICS
= 0x02;
45 inline bool lclNeedsRichTextFormat( const oox::xls::Font
* pFont
)
47 return pFont
&& pFont
->needsRichTextFormat();
52 RichStringPortion::RichStringPortion( const WorkbookHelper
& rHelper
) :
53 WorkbookHelper( rHelper
),
59 void RichStringPortion::setText( const OUString
& rText
)
64 FontRef
RichStringPortion::createFont()
66 mxFont
.reset( new Font( *this, false ) );
70 void RichStringPortion::setFontId( sal_Int32 nFontId
)
75 void RichStringPortion::finalizeImport()
78 mxFont
->finalizeImport();
79 else if( mnFontId
>= 0 )
80 mxFont
= getStyles().getFont( mnFontId
);
83 void RichStringPortion::convert( const Reference
< XText
>& rxText
, const oox::xls::Font
* pFont
, bool bReplace
)
88 Reference
< XTextRange
> xRange
;
90 xRange
.set( rxText
, UNO_QUERY
);
92 xRange
= rxText
->getEnd();
93 OSL_ENSURE( xRange
.is(), "RichStringPortion::convert - cannot get text range interface" );
97 xRange
->setString( maText
);
100 PropertySet
aPropSet( xRange
);
101 mxFont
->writeToPropertySet( aPropSet
, FONT_PROPTYPE_TEXT
);
104 /* Some font attributes cannot be set to cell formatting in Calc but
105 require to use rich formatting, e.g. font escapement. But do not
106 use the passed font if this portion has its own font. */
107 else if( lclNeedsRichTextFormat( pFont
) )
109 PropertySet
aPropSet( xRange
);
110 pFont
->writeToPropertySet( aPropSet
, FONT_PROPTYPE_TEXT
);
117 void RichStringPortion::convert( ScEditEngineDefaulter
& rEE
, ESelection
& rSelection
, const oox::xls::Font
* pFont
)
119 rSelection
.nStartPos
= rSelection
.nEndPos
;
120 rSelection
.nStartPara
= rSelection
.nEndPara
;
121 SfxItemSet
aItemSet( rEE
.GetEmptyItemSet() );
123 const Font
* pFontToUse
= mxFont
.get() ? mxFont
.get() : lclNeedsRichTextFormat( pFont
) ? pFont
: nullptr;
126 pFontToUse
->fillToItemSet( aItemSet
, true );
128 // #TODO need to manually adjust nEndPos ( and nEndPara ) to cater for any paragraphs
129 sal_Int32 nLastParaLoc
= -1;
130 sal_Int32 nSearchIndex
= maText
.indexOf( '\n' );
131 sal_Int32 nParaOccurence
= 0;
132 while ( nSearchIndex
!= -1 )
134 nLastParaLoc
= nSearchIndex
;
136 rSelection
.nEndPos
= 0;
137 nSearchIndex
= maText
.indexOf( '\n', nSearchIndex
+ 1);
140 rSelection
.nEndPara
+= nParaOccurence
;
141 if ( nLastParaLoc
!= -1 )
143 rSelection
.nEndPos
= maText
.getLength() - 1 - nLastParaLoc
;
147 rSelection
.nEndPos
= rSelection
.nStartPos
+ maText
.getLength();
149 rEE
.QuickSetAttribs( aItemSet
, rSelection
);
152 void RichStringPortion::writeFontProperties( const Reference
<XText
>& rxText
, const oox::xls::Font
* pFont
) const
154 PropertySet
aPropSet(rxText
);
157 mxFont
->writeToPropertySet(aPropSet
, FONT_PROPTYPE_TEXT
);
159 if (lclNeedsRichTextFormat(pFont
))
160 pFont
->writeToPropertySet(aPropSet
, FONT_PROPTYPE_TEXT
);
163 void FontPortionModel::read( SequenceInputStream
& rStrm
)
165 mnPos
= rStrm
.readuInt16();
166 mnFontId
= rStrm
.readuInt16();
169 void FontPortionModelList::appendPortion( const FontPortionModel
& rPortion
)
171 // #i33341# real life -- same character index may occur several times
172 OSL_ENSURE( mvModels
.empty() || (mvModels
.back().mnPos
<= rPortion
.mnPos
), "FontPortionModelList::appendPortion - wrong char order" );
173 if( mvModels
.empty() || (mvModels
.back().mnPos
< rPortion
.mnPos
) )
174 mvModels
.push_back( rPortion
);
176 mvModels
.back().mnFontId
= rPortion
.mnFontId
;
179 void FontPortionModelList::importPortions( SequenceInputStream
& rStrm
)
181 sal_Int32 nCount
= rStrm
.readInt32();
185 mvModels
.reserve( getLimitedValue
< size_t, sal_Int64
>( nCount
, 0, rStrm
.getRemaining() / 4 ) );
186 /* #i33341# real life -- same character index may occur several times
187 -> use appendPortion() to validate string position. */
188 FontPortionModel aPortion
;
189 for( sal_Int32 nIndex
= 0; !rStrm
.isEof() && (nIndex
< nCount
); ++nIndex
)
191 aPortion
.read( rStrm
);
192 appendPortion( aPortion
);
197 PhoneticDataModel::PhoneticDataModel() :
199 mnType( XML_fullwidthKatakana
),
200 mnAlignment( XML_left
)
204 void PhoneticDataModel::setBiffData( sal_Int32 nType
, sal_Int32 nAlignment
)
206 static const sal_Int32 spnTypeIds
[] = { XML_halfwidthKatakana
, XML_fullwidthKatakana
, XML_hiragana
, XML_noConversion
};
207 mnType
= STATIC_ARRAY_SELECT( spnTypeIds
, nType
, XML_fullwidthKatakana
);
209 static const sal_Int32 spnAlignments
[] = { XML_noControl
, XML_left
, XML_center
, XML_distributed
};
210 mnAlignment
= STATIC_ARRAY_SELECT( spnAlignments
, nAlignment
, XML_left
);
213 PhoneticSettings::PhoneticSettings( const WorkbookHelper
& rHelper
) :
214 WorkbookHelper( rHelper
)
218 void PhoneticSettings::importPhoneticPr( const AttributeList
& rAttribs
)
220 maModel
.mnFontId
= rAttribs
.getInteger( XML_fontId
, -1 );
221 maModel
.mnType
= rAttribs
.getToken( XML_type
, XML_fullwidthKatakana
);
222 maModel
.mnAlignment
= rAttribs
.getToken( XML_alignment
, XML_left
);
225 void PhoneticSettings::importPhoneticPr( SequenceInputStream
& rStrm
)
228 sal_Int32 nType
, nAlignment
;
229 nFontId
= rStrm
.readuInt16();
230 nType
= rStrm
.readInt32();
231 nAlignment
= rStrm
.readInt32();
232 maModel
.mnFontId
= nFontId
;
233 maModel
.setBiffData( nType
, nAlignment
);
236 void PhoneticSettings::importStringData( SequenceInputStream
& rStrm
)
238 sal_uInt16 nFontId
, nFlags
;
239 nFontId
= rStrm
.readuInt16();
240 nFlags
= rStrm
.readuInt16();
241 maModel
.mnFontId
= nFontId
;
242 maModel
.setBiffData( extractValue
< sal_Int32
>( nFlags
, 0, 2 ), extractValue
< sal_Int32
>( nFlags
, 2, 2 ) );
245 RichStringPhonetic::RichStringPhonetic( const WorkbookHelper
& rHelper
) :
246 WorkbookHelper( rHelper
),
252 void RichStringPhonetic::setText( const OUString
& rText
)
257 void RichStringPhonetic::importPhoneticRun( const AttributeList
& rAttribs
)
259 mnBasePos
= rAttribs
.getInteger( XML_sb
, -1 );
260 mnBaseEnd
= rAttribs
.getInteger( XML_eb
, -1 );
263 void RichStringPhonetic::setBaseRange( sal_Int32 nBasePos
, sal_Int32 nBaseEnd
)
265 mnBasePos
= nBasePos
;
266 mnBaseEnd
= nBaseEnd
;
269 void PhoneticPortionModel::read( SequenceInputStream
& rStrm
)
271 mnPos
= rStrm
.readuInt16();
272 mnBasePos
= rStrm
.readuInt16();
273 mnBaseLen
= rStrm
.readuInt16();
276 void PhoneticPortionModelList::appendPortion( const PhoneticPortionModel
& rPortion
)
278 // same character index may occur several times
279 OSL_ENSURE( mvModels
.empty() || ((mvModels
.back().mnPos
<= rPortion
.mnPos
) &&
280 (mvModels
.back().mnBasePos
+ mvModels
.back().mnBaseLen
<= rPortion
.mnBasePos
)),
281 "PhoneticPortionModelList::appendPortion - wrong char order" );
282 if( mvModels
.empty() || (mvModels
.back().mnPos
< rPortion
.mnPos
) )
284 mvModels
.push_back( rPortion
);
286 else if( mvModels
.back().mnPos
== rPortion
.mnPos
)
288 mvModels
.back().mnBasePos
= rPortion
.mnBasePos
;
289 mvModels
.back().mnBaseLen
= rPortion
.mnBaseLen
;
293 void PhoneticPortionModelList::importPortions( SequenceInputStream
& rStrm
)
295 sal_Int32 nCount
= rStrm
.readInt32();
299 mvModels
.reserve( getLimitedValue
< size_t, sal_Int64
>( nCount
, 0, rStrm
.getRemaining() / 6 ) );
300 PhoneticPortionModel aPortion
;
301 for( sal_Int32 nIndex
= 0; !rStrm
.isEof() && (nIndex
< nCount
); ++nIndex
)
303 aPortion
.read( rStrm
);
304 appendPortion( aPortion
);
309 RichString::RichString( const WorkbookHelper
& rHelper
) :
310 WorkbookHelper( rHelper
),
311 maPhonSettings( rHelper
)
315 RichStringPortionRef
RichString::importText( const AttributeList
& )
317 return createPortion();
320 RichStringPortionRef
RichString::importRun( const AttributeList
& )
322 return createPortion();
325 RichStringPhoneticRef
RichString::importPhoneticRun( const AttributeList
& rAttribs
)
327 RichStringPhoneticRef xPhonetic
= createPhonetic();
328 xPhonetic
->importPhoneticRun( rAttribs
);
332 void RichString::importPhoneticPr( const AttributeList
& rAttribs
)
334 maPhonSettings
.importPhoneticPr( rAttribs
);
337 void RichString::importString( SequenceInputStream
& rStrm
, bool bRich
)
339 sal_uInt8 nFlags
= bRich
? rStrm
.readuInt8() : 0;
340 OUString aBaseText
= BiffHelper::readString( rStrm
);
342 if( !rStrm
.isEof() && getFlag( nFlags
, BIFF12_STRINGFLAG_FONTS
) )
344 FontPortionModelList aPortions
;
345 aPortions
.importPortions( rStrm
);
346 createTextPortions( aBaseText
, aPortions
);
350 createPortion()->setText( aBaseText
);
353 if( !rStrm
.isEof() && getFlag( nFlags
, BIFF12_STRINGFLAG_PHONETICS
) )
355 OUString aPhoneticText
= BiffHelper::readString( rStrm
);
356 PhoneticPortionModelList aPortions
;
357 aPortions
.importPortions( rStrm
);
358 maPhonSettings
.importStringData( rStrm
);
359 createPhoneticPortions( aPhoneticText
, aPortions
, aBaseText
.getLength() );
363 void RichString::finalizeImport()
365 maTextPortions
.forEachMem( &RichStringPortion::finalizeImport
);
368 bool RichString::extractPlainString( OUString
& orString
, const oox::xls::Font
* pFirstPortionFont
) const
370 if( !maPhonPortions
.empty() )
372 if( maTextPortions
.empty() )
377 if( (maTextPortions
.size() == 1) && !maTextPortions
.front()->hasFont() && !lclNeedsRichTextFormat( pFirstPortionFont
) )
379 orString
= maTextPortions
.front()->getText();
380 return orString
.indexOf( '\x0A' ) < 0;
385 void RichString::convert( const Reference
< XText
>& rxText
, bool bReplaceOld
) const
387 if (maTextPortions
.size() == 1)
389 // Set text directly to the cell when the string has only one portion.
390 // It's much faster this way.
391 RichStringPortion
& rPtn
= *maTextPortions
.front();
392 rxText
->setString(rPtn
.getText());
393 rPtn
.writeFontProperties(rxText
, nullptr);
397 for( PortionVector::const_iterator aIt
= maTextPortions
.begin(), aEnd
= maTextPortions
.end(); aIt
!= aEnd
; ++aIt
)
399 (*aIt
)->convert( rxText
, nullptr, bReplaceOld
);
400 bReplaceOld
= false; // do not replace first portion text with following portions
404 ::EditTextObject
* RichString::convert( ScEditEngineDefaulter
& rEE
, const oox::xls::Font
* pFirstPortionFont
) const
406 ESelection aSelection
;
409 for( PortionVector::const_iterator aIt
= maTextPortions
.begin(), aEnd
= maTextPortions
.end(); aIt
!= aEnd
; ++aIt
)
410 sString
+= (*aIt
)->getText();
412 // fdo#84370 - diving into editeng is not thread safe.
413 SolarMutexGuard aGuard
;
415 rEE
.SetText( sString
);
417 for( PortionVector::const_iterator aIt
= maTextPortions
.begin(), aEnd
= maTextPortions
.end(); aIt
!= aEnd
; ++aIt
)
419 (*aIt
)->convert( rEE
, aSelection
, pFirstPortionFont
);
420 pFirstPortionFont
= nullptr;
423 return rEE
.CreateTextObject();
426 // private --------------------------------------------------------------------
428 RichStringPortionRef
RichString::createPortion()
430 RichStringPortionRef
xPortion( new RichStringPortion( *this ) );
431 maTextPortions
.push_back( xPortion
);
435 RichStringPhoneticRef
RichString::createPhonetic()
437 RichStringPhoneticRef
xPhonetic( new RichStringPhonetic( *this ) );
438 maPhonPortions
.push_back( xPhonetic
);
442 void RichString::createTextPortions( const OUString
& rText
, FontPortionModelList
& rPortions
)
444 maTextPortions
.clear();
445 if( !rText
.isEmpty() )
447 sal_Int32 nStrLen
= rText
.getLength();
448 // add leading and trailing string position to ease the following loop
449 if( rPortions
.empty() || (rPortions
.front().mnPos
> 0) )
450 rPortions
.insert( rPortions
.begin(), FontPortionModel( 0, -1 ) );
451 if( rPortions
.back().mnPos
< nStrLen
)
452 rPortions
.push_back( FontPortionModel( nStrLen
, -1 ) );
454 // create all string portions according to the font id vector
455 for( ::std::vector
< FontPortionModel
>::const_iterator aIt
= rPortions
.begin(); aIt
->mnPos
< nStrLen
; ++aIt
)
457 sal_Int32 nPortionLen
= (aIt
+ 1)->mnPos
- aIt
->mnPos
;
458 if( (0 < nPortionLen
) && (aIt
->mnPos
+ nPortionLen
<= nStrLen
) )
460 RichStringPortionRef xPortion
= createPortion();
461 xPortion
->setText( rText
.copy( aIt
->mnPos
, nPortionLen
) );
462 xPortion
->setFontId( aIt
->mnFontId
);
468 void RichString::createPhoneticPortions( const OUString
& rText
, PhoneticPortionModelList
& rPortions
, sal_Int32 nBaseLen
)
470 maPhonPortions
.clear();
471 if( !rText
.isEmpty())
473 sal_Int32 nStrLen
= rText
.getLength();
474 // no portions - assign phonetic text to entire base text
475 if( rPortions
.empty() )
476 rPortions
.push_back( PhoneticPortionModel( 0, 0, nBaseLen
) );
477 // add trailing string position to ease the following loop
478 if( rPortions
.back().mnPos
< nStrLen
)
479 rPortions
.push_back( PhoneticPortionModel( nStrLen
, nBaseLen
, 0 ) );
481 // create all phonetic portions according to the portions vector
482 for( ::std::vector
< PhoneticPortionModel
>::const_iterator aIt
= rPortions
.begin(); aIt
->mnPos
< nStrLen
; ++aIt
)
484 sal_Int32 nPortionLen
= (aIt
+ 1)->mnPos
- aIt
->mnPos
;
485 if( (0 < nPortionLen
) && (aIt
->mnPos
+ nPortionLen
<= nStrLen
) )
487 RichStringPhoneticRef xPhonetic
= createPhonetic();
488 xPhonetic
->setText( rText
.copy( aIt
->mnPos
, nPortionLen
) );
489 xPhonetic
->setBaseRange( aIt
->mnBasePos
, aIt
->mnBasePos
+ aIt
->mnBaseLen
);
498 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */