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/text/XText.hpp>
23 #include <rtl/ustrbuf.hxx>
24 #include <editeng/editobj.hxx>
25 #include <oox/helper/attributelist.hxx>
26 #include <oox/helper/propertyset.hxx>
27 #include "biffinputstream.hxx"
28 #include "editutil.hxx"
30 #include <vcl/svapp.hxx>
35 using namespace ::com::sun::star::text
;
36 using namespace ::com::sun::star::uno
;
40 const sal_uInt8 BIFF12_STRINGFLAG_FONTS
= 0x01;
41 const sal_uInt8 BIFF12_STRINGFLAG_PHONETICS
= 0x02;
43 inline bool lclNeedsRichTextFormat( const oox::xls::Font
* pFont
)
45 return pFont
&& pFont
->needsRichTextFormat();
50 RichStringPortion::RichStringPortion( const WorkbookHelper
& rHelper
) :
51 WorkbookHelper( rHelper
),
57 void RichStringPortion::setText( const OUString
& rText
)
62 FontRef
RichStringPortion::createFont()
64 mxFont
.reset( new Font( *this, false ) );
68 void RichStringPortion::setFontId( sal_Int32 nFontId
)
73 void RichStringPortion::finalizeImport()
76 mxFont
->finalizeImport();
77 else if( mnFontId
>= 0 )
78 mxFont
= getStyles().getFont( mnFontId
);
81 void RichStringPortion::convert( const Reference
< XText
>& rxText
, const oox::xls::Font
* pFont
, bool bReplace
)
86 Reference
< XTextRange
> xRange
;
88 xRange
.set( rxText
, UNO_QUERY
);
90 xRange
= rxText
->getEnd();
91 OSL_ENSURE( xRange
.is(), "RichStringPortion::convert - cannot get text range interface" );
95 xRange
->setString( maText
);
98 PropertySet
aPropSet( xRange
);
99 mxFont
->writeToPropertySet( aPropSet
, FONT_PROPTYPE_TEXT
);
102 /* Some font attributes cannot be set to cell formatting in Calc but
103 require to use rich formatting, e.g. font escapement. But do not
104 use the passed font if this portion has its own font. */
105 else if( lclNeedsRichTextFormat( pFont
) )
107 PropertySet
aPropSet( xRange
);
108 pFont
->writeToPropertySet( aPropSet
, FONT_PROPTYPE_TEXT
);
115 void RichStringPortion::convert( ScEditEngineDefaulter
& rEE
, ESelection
& rSelection
, const oox::xls::Font
* pFont
)
117 rSelection
.nStartPos
= rSelection
.nEndPos
;
118 rSelection
.nStartPara
= rSelection
.nEndPara
;
119 SfxItemSet
aItemSet( rEE
.GetEmptyItemSet() );
121 const Font
* pFontToUse
= mxFont
.get() ? mxFont
.get() : lclNeedsRichTextFormat( pFont
) ? pFont
: NULL
;
124 pFontToUse
->fillToItemSet( aItemSet
, true );
126 // #TODO need to manually adjust nEndPos ( and nEndPara ) to cater for any paragraphs
127 sal_Int32 nLastParaLoc
= -1;
128 sal_Int32 nSearchIndex
= maText
.indexOf( '\n' );
129 sal_Int32 nParaOccurence
= 0;
130 while ( nSearchIndex
!= -1 )
132 nLastParaLoc
= nSearchIndex
;
134 rSelection
.nEndPos
= 0;
135 nSearchIndex
= maText
.indexOf( '\n', nSearchIndex
+ 1);
138 rSelection
.nEndPara
+= nParaOccurence
;
139 if ( nLastParaLoc
!= -1 )
141 rSelection
.nEndPos
= maText
.getLength() - 1 - nLastParaLoc
;
145 rSelection
.nEndPos
= rSelection
.nStartPos
+ maText
.getLength();
147 rEE
.QuickSetAttribs( aItemSet
, rSelection
);
150 void RichStringPortion::writeFontProperties( const Reference
<XText
>& rxText
, const oox::xls::Font
* pFont
) const
152 PropertySet
aPropSet(rxText
);
155 mxFont
->writeToPropertySet(aPropSet
, FONT_PROPTYPE_TEXT
);
157 if (lclNeedsRichTextFormat(pFont
))
158 pFont
->writeToPropertySet(aPropSet
, FONT_PROPTYPE_TEXT
);
161 void FontPortionModel::read( SequenceInputStream
& rStrm
)
163 mnPos
= rStrm
.readuInt16();
164 mnFontId
= rStrm
.readuInt16();
167 void FontPortionModelList::appendPortion( const FontPortionModel
& rPortion
)
169 // #i33341# real life -- same character index may occur several times
170 OSL_ENSURE( mvModels
.empty() || (mvModels
.back().mnPos
<= rPortion
.mnPos
), "FontPortionModelList::appendPortion - wrong char order" );
171 if( mvModels
.empty() || (mvModels
.back().mnPos
< rPortion
.mnPos
) )
172 mvModels
.push_back( rPortion
);
174 mvModels
.back().mnFontId
= rPortion
.mnFontId
;
177 void FontPortionModelList::importPortions( SequenceInputStream
& rStrm
)
179 sal_Int32 nCount
= rStrm
.readInt32();
183 mvModels
.reserve( getLimitedValue
< size_t, sal_Int64
>( nCount
, 0, rStrm
.getRemaining() / 4 ) );
184 /* #i33341# real life -- same character index may occur several times
185 -> use appendPortion() to validate string position. */
186 FontPortionModel aPortion
;
187 for( sal_Int32 nIndex
= 0; !rStrm
.isEof() && (nIndex
< nCount
); ++nIndex
)
189 aPortion
.read( rStrm
);
190 appendPortion( aPortion
);
195 PhoneticDataModel::PhoneticDataModel() :
197 mnType( XML_fullwidthKatakana
),
198 mnAlignment( XML_left
)
202 void PhoneticDataModel::setBiffData( sal_Int32 nType
, sal_Int32 nAlignment
)
204 static const sal_Int32 spnTypeIds
[] = { XML_halfwidthKatakana
, XML_fullwidthKatakana
, XML_hiragana
, XML_noConversion
};
205 mnType
= STATIC_ARRAY_SELECT( spnTypeIds
, nType
, XML_fullwidthKatakana
);
207 static const sal_Int32 spnAlignments
[] = { XML_noControl
, XML_left
, XML_center
, XML_distributed
};
208 mnAlignment
= STATIC_ARRAY_SELECT( spnAlignments
, nAlignment
, XML_left
);
211 PhoneticSettings::PhoneticSettings( const WorkbookHelper
& rHelper
) :
212 WorkbookHelper( rHelper
)
216 void PhoneticSettings::importPhoneticPr( const AttributeList
& rAttribs
)
218 maModel
.mnFontId
= rAttribs
.getInteger( XML_fontId
, -1 );
219 maModel
.mnType
= rAttribs
.getToken( XML_type
, XML_fullwidthKatakana
);
220 maModel
.mnAlignment
= rAttribs
.getToken( XML_alignment
, XML_left
);
223 void PhoneticSettings::importPhoneticPr( SequenceInputStream
& rStrm
)
226 sal_Int32 nType
, nAlignment
;
227 nFontId
= rStrm
.readuInt16();
228 nType
= rStrm
.readInt32();
229 nAlignment
= rStrm
.readInt32();
230 maModel
.mnFontId
= nFontId
;
231 maModel
.setBiffData( nType
, nAlignment
);
234 void PhoneticSettings::importStringData( SequenceInputStream
& rStrm
)
236 sal_uInt16 nFontId
, nFlags
;
237 nFontId
= rStrm
.readuInt16();
238 nFlags
= rStrm
.readuInt16();
239 maModel
.mnFontId
= nFontId
;
240 maModel
.setBiffData( extractValue
< sal_Int32
>( nFlags
, 0, 2 ), extractValue
< sal_Int32
>( nFlags
, 2, 2 ) );
243 RichStringPhonetic::RichStringPhonetic( const WorkbookHelper
& rHelper
) :
244 WorkbookHelper( rHelper
),
250 void RichStringPhonetic::setText( const OUString
& rText
)
255 void RichStringPhonetic::importPhoneticRun( const AttributeList
& rAttribs
)
257 mnBasePos
= rAttribs
.getInteger( XML_sb
, -1 );
258 mnBaseEnd
= rAttribs
.getInteger( XML_eb
, -1 );
261 void RichStringPhonetic::setBaseRange( sal_Int32 nBasePos
, sal_Int32 nBaseEnd
)
263 mnBasePos
= nBasePos
;
264 mnBaseEnd
= nBaseEnd
;
267 void PhoneticPortionModel::read( SequenceInputStream
& rStrm
)
269 mnPos
= rStrm
.readuInt16();
270 mnBasePos
= rStrm
.readuInt16();
271 mnBaseLen
= rStrm
.readuInt16();
274 void PhoneticPortionModelList::appendPortion( const PhoneticPortionModel
& rPortion
)
276 // same character index may occur several times
277 OSL_ENSURE( mvModels
.empty() || ((mvModels
.back().mnPos
<= rPortion
.mnPos
) &&
278 (mvModels
.back().mnBasePos
+ mvModels
.back().mnBaseLen
<= rPortion
.mnBasePos
)),
279 "PhoneticPortionModelList::appendPortion - wrong char order" );
280 if( mvModels
.empty() || (mvModels
.back().mnPos
< rPortion
.mnPos
) )
282 mvModels
.push_back( rPortion
);
284 else if( mvModels
.back().mnPos
== rPortion
.mnPos
)
286 mvModels
.back().mnBasePos
= rPortion
.mnBasePos
;
287 mvModels
.back().mnBaseLen
= rPortion
.mnBaseLen
;
291 void PhoneticPortionModelList::importPortions( SequenceInputStream
& rStrm
)
293 sal_Int32 nCount
= rStrm
.readInt32();
297 mvModels
.reserve( getLimitedValue
< size_t, sal_Int64
>( nCount
, 0, rStrm
.getRemaining() / 6 ) );
298 PhoneticPortionModel aPortion
;
299 for( sal_Int32 nIndex
= 0; !rStrm
.isEof() && (nIndex
< nCount
); ++nIndex
)
301 aPortion
.read( rStrm
);
302 appendPortion( aPortion
);
307 RichString::RichString( const WorkbookHelper
& rHelper
) :
308 WorkbookHelper( rHelper
),
309 maPhonSettings( rHelper
)
313 RichStringPortionRef
RichString::importText( const AttributeList
& )
315 return createPortion();
318 RichStringPortionRef
RichString::importRun( const AttributeList
& )
320 return createPortion();
323 RichStringPhoneticRef
RichString::importPhoneticRun( const AttributeList
& rAttribs
)
325 RichStringPhoneticRef xPhonetic
= createPhonetic();
326 xPhonetic
->importPhoneticRun( rAttribs
);
330 void RichString::importPhoneticPr( const AttributeList
& rAttribs
)
332 maPhonSettings
.importPhoneticPr( rAttribs
);
335 void RichString::importString( SequenceInputStream
& rStrm
, bool bRich
)
337 sal_uInt8 nFlags
= bRich
? rStrm
.readuInt8() : 0;
338 OUString aBaseText
= BiffHelper::readString( rStrm
);
340 if( !rStrm
.isEof() && getFlag( nFlags
, BIFF12_STRINGFLAG_FONTS
) )
342 FontPortionModelList aPortions
;
343 aPortions
.importPortions( rStrm
);
344 createTextPortions( aBaseText
, aPortions
);
348 createPortion()->setText( aBaseText
);
351 if( !rStrm
.isEof() && getFlag( nFlags
, BIFF12_STRINGFLAG_PHONETICS
) )
353 OUString aPhoneticText
= BiffHelper::readString( rStrm
);
354 PhoneticPortionModelList aPortions
;
355 aPortions
.importPortions( rStrm
);
356 maPhonSettings
.importStringData( rStrm
);
357 createPhoneticPortions( aPhoneticText
, aPortions
, aBaseText
.getLength() );
361 void RichString::finalizeImport()
363 maTextPortions
.forEachMem( &RichStringPortion::finalizeImport
);
366 bool RichString::extractPlainString( OUString
& orString
, const oox::xls::Font
* pFirstPortionFont
) const
368 if( !maPhonPortions
.empty() )
370 if( maTextPortions
.empty() )
375 if( (maTextPortions
.size() == 1) && !maTextPortions
.front()->hasFont() && !lclNeedsRichTextFormat( pFirstPortionFont
) )
377 orString
= maTextPortions
.front()->getText();
378 return orString
.indexOf( '\x0A' ) < 0;
383 void RichString::convert( const Reference
< XText
>& rxText
, bool bReplaceOld
, const oox::xls::Font
* pFirstPortionFont
) const
385 if (maTextPortions
.size() == 1)
387 // Set text directly to the cell when the string has only one portion.
388 // It's much faster this way.
389 RichStringPortion
& rPtn
= *maTextPortions
.front();
390 rxText
->setString(rPtn
.getText());
391 rPtn
.writeFontProperties(rxText
, pFirstPortionFont
);
395 for( PortionVector::const_iterator aIt
= maTextPortions
.begin(), aEnd
= maTextPortions
.end(); aIt
!= aEnd
; ++aIt
)
397 (*aIt
)->convert( rxText
, pFirstPortionFont
, bReplaceOld
);
398 pFirstPortionFont
= 0; // use passed font for first portion only
399 bReplaceOld
= false; // do not replace first portion text with following portions
403 ::EditTextObject
* RichString::convert( ScEditEngineDefaulter
& rEE
, const oox::xls::Font
* pFirstPortionFont
) const
405 ESelection aSelection
;
408 for( PortionVector::const_iterator aIt
= maTextPortions
.begin(), aEnd
= maTextPortions
.end(); aIt
!= aEnd
; ++aIt
)
409 sString
+= (*aIt
)->getText();
411 // fdo#84370 - diving into editeng is not thread safe.
412 SolarMutexGuard aGuard
;
414 rEE
.SetText( sString
);
416 for( PortionVector::const_iterator aIt
= maTextPortions
.begin(), aEnd
= maTextPortions
.end(); aIt
!= aEnd
; ++aIt
)
418 (*aIt
)->convert( rEE
, aSelection
, pFirstPortionFont
);
419 pFirstPortionFont
= 0;
422 return rEE
.CreateTextObject();
425 // private --------------------------------------------------------------------
427 RichStringPortionRef
RichString::createPortion()
429 RichStringPortionRef
xPortion( new RichStringPortion( *this ) );
430 maTextPortions
.push_back( xPortion
);
434 RichStringPhoneticRef
RichString::createPhonetic()
436 RichStringPhoneticRef
xPhonetic( new RichStringPhonetic( *this ) );
437 maPhonPortions
.push_back( xPhonetic
);
441 void RichString::createTextPortions( const OUString
& rText
, FontPortionModelList
& rPortions
)
443 maTextPortions
.clear();
444 if( !rText
.isEmpty() )
446 sal_Int32 nStrLen
= rText
.getLength();
447 // add leading and trailing string position to ease the following loop
448 if( rPortions
.empty() || (rPortions
.front().mnPos
> 0) )
449 rPortions
.insert( rPortions
.begin(), FontPortionModel( 0, -1 ) );
450 if( rPortions
.back().mnPos
< nStrLen
)
451 rPortions
.push_back( FontPortionModel( nStrLen
, -1 ) );
453 // create all string portions according to the font id vector
454 for( ::std::vector
< FontPortionModel
>::const_iterator aIt
= rPortions
.begin(); aIt
->mnPos
< nStrLen
; ++aIt
)
456 sal_Int32 nPortionLen
= (aIt
+ 1)->mnPos
- aIt
->mnPos
;
457 if( (0 < nPortionLen
) && (aIt
->mnPos
+ nPortionLen
<= nStrLen
) )
459 RichStringPortionRef xPortion
= createPortion();
460 xPortion
->setText( rText
.copy( aIt
->mnPos
, nPortionLen
) );
461 xPortion
->setFontId( aIt
->mnFontId
);
467 void RichString::createPhoneticPortions( const OUString
& rText
, PhoneticPortionModelList
& rPortions
, sal_Int32 nBaseLen
)
469 maPhonPortions
.clear();
470 if( !rText
.isEmpty())
472 sal_Int32 nStrLen
= rText
.getLength();
473 // no portions - assign phonetic text to entire base text
474 if( rPortions
.empty() )
475 rPortions
.push_back( PhoneticPortionModel( 0, 0, nBaseLen
) );
476 // add trailing string position to ease the following loop
477 if( rPortions
.back().mnPos
< nStrLen
)
478 rPortions
.push_back( PhoneticPortionModel( nStrLen
, nBaseLen
, 0 ) );
480 // create all phonetic portions according to the portions vector
481 for( ::std::vector
< PhoneticPortionModel
>::const_iterator aIt
= rPortions
.begin(); aIt
->mnPos
< nStrLen
; ++aIt
)
483 sal_Int32 nPortionLen
= (aIt
+ 1)->mnPos
- aIt
->mnPos
;
484 if( (0 < nPortionLen
) && (aIt
->mnPos
+ nPortionLen
<= nStrLen
) )
486 RichStringPhoneticRef xPhonetic
= createPhonetic();
487 xPhonetic
->setText( rText
.copy( aIt
->mnPos
, nPortionLen
) );
488 xPhonetic
->setBaseRange( aIt
->mnBasePos
, aIt
->mnBasePos
+ aIt
->mnBaseLen
);
497 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */