xref: /aoo41x/main/oox/source/xls/richstring.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "oox/xls/richstring.hxx"
29 
30 #include <com/sun/star/text/XText.hpp>
31 #include <rtl/ustrbuf.hxx>
32 #include "oox/helper/attributelist.hxx"
33 #include "oox/helper/propertyset.hxx"
34 #include "oox/xls/biffinputstream.hxx"
35 
36 namespace oox {
37 namespace xls {
38 
39 // ============================================================================
40 
41 using namespace ::com::sun::star::text;
42 using namespace ::com::sun::star::uno;
43 
44 using ::rtl::OString;
45 using ::rtl::OUString;
46 using ::rtl::OUStringBuffer;
47 
48 // ============================================================================
49 
50 namespace {
51 
52 const sal_uInt8 BIFF12_STRINGFLAG_FONTS         = 0x01;
53 const sal_uInt8 BIFF12_STRINGFLAG_PHONETICS     = 0x02;
54 
55 inline bool lclNeedsRichTextFormat( const Font* pFont )
56 {
57     return pFont && pFont->needsRichTextFormat();
58 }
59 
60 } // namespace
61 
62 // ============================================================================
63 
64 RichStringPortion::RichStringPortion( const WorkbookHelper& rHelper ) :
65     WorkbookHelper( rHelper ),
66     mnFontId( -1 )
67 {
68 }
69 
70 void RichStringPortion::setText( const OUString& rText )
71 {
72     maText = rText;
73 }
74 
75 FontRef RichStringPortion::createFont()
76 {
77     mxFont.reset( new Font( *this, false ) );
78     return mxFont;
79 }
80 
81 void RichStringPortion::setFontId( sal_Int32 nFontId )
82 {
83     mnFontId = nFontId;
84 }
85 
86 void RichStringPortion::finalizeImport()
87 {
88     if( mxFont.get() )
89         mxFont->finalizeImport();
90     else if( mnFontId >= 0 )
91         mxFont = getStyles().getFont( mnFontId );
92 }
93 
94 void RichStringPortion::convert( const Reference< XText >& rxText, const Font* pFont, bool bReplace )
95 {
96     Reference< XTextRange > xRange;
97     if( bReplace )
98         xRange.set( rxText, UNO_QUERY );
99     else
100         xRange = rxText->getEnd();
101     OSL_ENSURE( xRange.is(), "RichStringPortion::convert - cannot get text range interface" );
102 
103     if( xRange.is() )
104     {
105         xRange->setString( maText );
106         if( mxFont.get() )
107         {
108             PropertySet aPropSet( xRange );
109             mxFont->writeToPropertySet( aPropSet, FONT_PROPTYPE_TEXT );
110         }
111 
112         /*  Some font attributes cannot be set to cell formattiing in Calc but
113             require to use rich formatting, e.g. font escapement. */
114         if( lclNeedsRichTextFormat( pFont ) )
115         {
116             PropertySet aPropSet( xRange );
117             pFont->writeToPropertySet( aPropSet, FONT_PROPTYPE_TEXT );
118         }
119     }
120 }
121 
122 // ----------------------------------------------------------------------------
123 
124 void FontPortionModel::read( SequenceInputStream& rStrm )
125 {
126     mnPos = rStrm.readuInt16();
127     mnFontId = rStrm.readuInt16();
128 }
129 
130 void FontPortionModel::read( BiffInputStream& rStrm, BiffFontPortionMode eMode )
131 {
132     switch( eMode )
133     {
134         case BIFF_FONTPORTION_8BIT:
135             mnPos = rStrm.readuInt8();
136             mnFontId = rStrm.readuInt8();
137         break;
138         case BIFF_FONTPORTION_16BIT:
139             mnPos = rStrm.readuInt16();
140             mnFontId = rStrm.readuInt16();
141         break;
142         case BIFF_FONTPORTION_OBJ:
143             mnPos = rStrm.readuInt16();
144             mnFontId = rStrm.readuInt16();
145             rStrm.skip( 4 );
146         break;
147     }
148 }
149 
150 // ----------------------------------------------------------------------------
151 
152 void FontPortionModelList::appendPortion( const FontPortionModel& rPortion )
153 {
154     // #i33341# real life -- same character index may occur several times
155     OSL_ENSURE( empty() || (back().mnPos <= rPortion.mnPos), "FontPortionModelList::appendPortion - wrong char order" );
156     if( empty() || (back().mnPos < rPortion.mnPos) )
157         push_back( rPortion );
158     else
159         back().mnFontId = rPortion.mnFontId;
160 }
161 
162 void FontPortionModelList::importPortions( SequenceInputStream& rStrm )
163 {
164     sal_Int32 nCount = rStrm.readInt32();
165     clear();
166     if( nCount > 0 )
167     {
168         reserve( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 4 ) );
169         /*  #i33341# real life -- same character index may occur several times
170             -> use appendPortion() to validate string position. */
171         FontPortionModel aPortion;
172         for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
173         {
174             aPortion.read( rStrm );
175             appendPortion( aPortion );
176         }
177     }
178 }
179 
180 void FontPortionModelList::importPortions( BiffInputStream& rStrm, sal_uInt16 nCount, BiffFontPortionMode eMode )
181 {
182     clear();
183     reserve( nCount );
184     /*  #i33341# real life -- same character index may occur several times
185         -> use appendPortion() to validate string position. */
186     FontPortionModel aPortion;
187     for( sal_uInt16 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
188     {
189         aPortion.read( rStrm, eMode );
190         appendPortion( aPortion );
191     }
192 }
193 
194 void FontPortionModelList::importPortions( BiffInputStream& rStrm, bool b16Bit )
195 {
196     sal_uInt16 nCount = b16Bit ? rStrm.readuInt16() : rStrm.readuInt8();
197     importPortions( rStrm, nCount, b16Bit ? BIFF_FONTPORTION_16BIT : BIFF_FONTPORTION_8BIT );
198 }
199 
200 // ============================================================================
201 
202 PhoneticDataModel::PhoneticDataModel() :
203     mnFontId( -1 ),
204     mnType( XML_fullwidthKatakana ),
205     mnAlignment( XML_left )
206 {
207 }
208 
209 void PhoneticDataModel::setBiffData( sal_Int32 nType, sal_Int32 nAlignment )
210 {
211     static const sal_Int32 spnTypeIds[] = { XML_halfwidthKatakana, XML_fullwidthKatakana, XML_hiragana, XML_noConversion };
212     mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_fullwidthKatakana );
213 
214     static const sal_Int32 spnAlignments[] = { XML_noControl, XML_left, XML_center, XML_distributed };
215     mnAlignment = STATIC_ARRAY_SELECT( spnAlignments, nAlignment, XML_left );
216 }
217 
218 // ----------------------------------------------------------------------------
219 
220 PhoneticSettings::PhoneticSettings( const WorkbookHelper& rHelper ) :
221     WorkbookHelper( rHelper )
222 {
223 }
224 
225 void PhoneticSettings::importPhoneticPr( const AttributeList& rAttribs )
226 {
227     maModel.mnFontId    = rAttribs.getInteger( XML_fontId, -1 );
228     maModel.mnType      = rAttribs.getToken( XML_type, XML_fullwidthKatakana );
229     maModel.mnAlignment = rAttribs.getToken( XML_alignment, XML_left );
230 }
231 
232 void PhoneticSettings::importPhoneticPr( SequenceInputStream& rStrm )
233 {
234     sal_uInt16 nFontId;
235     sal_Int32 nType, nAlignment;
236     rStrm >> nFontId >> nType >> nAlignment;
237     maModel.mnFontId = nFontId;
238     maModel.setBiffData( nType, nAlignment );
239 }
240 
241 void PhoneticSettings::importPhoneticPr( BiffInputStream& rStrm )
242 {
243     sal_uInt16 nFontId, nFlags;
244     rStrm >> nFontId >> nFlags;
245     maModel.mnFontId = nFontId;
246     maModel.setBiffData( extractValue< sal_Int32 >( nFlags, 0, 2 ), extractValue< sal_Int32 >( nFlags, 2, 2 ) );
247     // following: range list with cells showing phonetic text
248 }
249 
250 void PhoneticSettings::importStringData( SequenceInputStream& rStrm )
251 {
252     sal_uInt16 nFontId, nFlags;
253     rStrm >> nFontId >> nFlags;
254     maModel.mnFontId = nFontId;
255     maModel.setBiffData( extractValue< sal_Int32 >( nFlags, 0, 2 ), extractValue< sal_Int32 >( nFlags, 2, 2 ) );
256 }
257 
258 void PhoneticSettings::importStringData( BiffInputStream& rStrm )
259 {
260     sal_uInt16 nFontId, nFlags;
261     rStrm >> nFontId >> nFlags;
262     maModel.mnFontId = nFontId;
263     maModel.setBiffData( extractValue< sal_Int32 >( nFlags, 0, 2 ), extractValue< sal_Int32 >( nFlags, 2, 2 ) );
264 }
265 
266 // ============================================================================
267 
268 RichStringPhonetic::RichStringPhonetic( const WorkbookHelper& rHelper ) :
269     WorkbookHelper( rHelper ),
270     mnBasePos( -1 ),
271     mnBaseEnd( -1 )
272 {
273 }
274 
275 void RichStringPhonetic::setText( const OUString& rText )
276 {
277     maText = rText;
278 }
279 
280 void RichStringPhonetic::importPhoneticRun( const AttributeList& rAttribs )
281 {
282     mnBasePos = rAttribs.getInteger( XML_sb, -1 );
283     mnBaseEnd = rAttribs.getInteger( XML_eb, -1 );
284 }
285 
286 void RichStringPhonetic::setBaseRange( sal_Int32 nBasePos, sal_Int32 nBaseEnd )
287 {
288     mnBasePos = nBasePos;
289     mnBaseEnd = nBaseEnd;
290 }
291 
292 // ----------------------------------------------------------------------------
293 
294 void PhoneticPortionModel::read( SequenceInputStream& rStrm )
295 {
296     mnPos = rStrm.readuInt16();
297     mnBasePos = rStrm.readuInt16();
298     mnBaseLen = rStrm.readuInt16();
299 }
300 
301 void PhoneticPortionModel::read( BiffInputStream& rStrm )
302 {
303     mnPos = rStrm.readuInt16();
304     mnBasePos = rStrm.readuInt16();
305     mnBaseLen = rStrm.readuInt16();
306 }
307 
308 // ----------------------------------------------------------------------------
309 
310 void PhoneticPortionModelList::appendPortion( const PhoneticPortionModel& rPortion )
311 {
312     // same character index may occur several times
313     OSL_ENSURE( empty() || ((back().mnPos <= rPortion.mnPos) &&
314         (back().mnBasePos + back().mnBaseLen <= rPortion.mnBasePos)),
315         "PhoneticPortionModelList::appendPortion - wrong char order" );
316     if( empty() || (back().mnPos < rPortion.mnPos) )
317     {
318         push_back( rPortion );
319     }
320     else if( back().mnPos == rPortion.mnPos )
321     {
322         back().mnBasePos = rPortion.mnBasePos;
323         back().mnBaseLen = rPortion.mnBaseLen;
324     }
325 }
326 
327 void PhoneticPortionModelList::importPortions( SequenceInputStream& rStrm )
328 {
329     sal_Int32 nCount = rStrm.readInt32();
330     clear();
331     if( nCount > 0 )
332     {
333         reserve( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 6 ) );
334         PhoneticPortionModel aPortion;
335         for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
336         {
337             aPortion.read( rStrm );
338             appendPortion( aPortion );
339         }
340     }
341 }
342 
343 OUString PhoneticPortionModelList::importPortions( BiffInputStream& rStrm, sal_Int32 nPhoneticSize )
344 {
345     OUString aPhoneticText;
346     sal_uInt16 nPortionCount, nTextLen1, nTextLen2;
347     rStrm >> nPortionCount >> nTextLen1 >> nTextLen2;
348     OSL_ENSURE( nTextLen1 == nTextLen2, "PhoneticPortionModelList::importPortions - wrong phonetic text length" );
349     if( (nTextLen1 == nTextLen2) && (nTextLen1 > 0) )
350     {
351         sal_Int32 nMinSize = 2 * nTextLen1 + 6 * nPortionCount + 14;
352         OSL_ENSURE( nMinSize <= nPhoneticSize, "PhoneticPortionModelList::importPortions - wrong size of phonetic data" );
353         if( nMinSize <= nPhoneticSize )
354         {
355             aPhoneticText = rStrm.readUnicodeArray( nTextLen1 );
356             clear();
357             reserve( nPortionCount );
358             PhoneticPortionModel aPortion;
359             for( sal_uInt16 nPortion = 0; nPortion < nPortionCount; ++nPortion )
360             {
361                 aPortion.read( rStrm );
362                 appendPortion( aPortion );
363             }
364         }
365     }
366     return aPhoneticText;
367 }
368 
369 // ============================================================================
370 
371 RichString::RichString( const WorkbookHelper& rHelper ) :
372     WorkbookHelper( rHelper ),
373     maPhonSettings( rHelper )
374 {
375 }
376 
377 RichStringPortionRef RichString::importText( const AttributeList& )
378 {
379     return createPortion();
380 }
381 
382 RichStringPortionRef RichString::importRun( const AttributeList& )
383 {
384     return createPortion();
385 }
386 
387 RichStringPhoneticRef RichString::importPhoneticRun( const AttributeList& rAttribs )
388 {
389     RichStringPhoneticRef xPhonetic = createPhonetic();
390     xPhonetic->importPhoneticRun( rAttribs );
391     return xPhonetic;
392 }
393 
394 void RichString::importPhoneticPr( const AttributeList& rAttribs )
395 {
396     maPhonSettings.importPhoneticPr( rAttribs );
397 }
398 
399 void RichString::importString( SequenceInputStream& rStrm, bool bRich )
400 {
401     sal_uInt8 nFlags = bRich ? rStrm.readuInt8() : 0;
402     OUString aBaseText = BiffHelper::readString( rStrm );
403 
404     if( !rStrm.isEof() && getFlag( nFlags, BIFF12_STRINGFLAG_FONTS ) )
405     {
406         FontPortionModelList aPortions;
407         aPortions.importPortions( rStrm );
408         createTextPortions( aBaseText, aPortions );
409     }
410     else
411     {
412         createPortion()->setText( aBaseText );
413     }
414 
415     if( !rStrm.isEof() && getFlag( nFlags, BIFF12_STRINGFLAG_PHONETICS ) )
416     {
417         OUString aPhoneticText = BiffHelper::readString( rStrm );
418         PhoneticPortionModelList aPortions;
419         aPortions.importPortions( rStrm );
420         maPhonSettings.importStringData( rStrm );
421         createPhoneticPortions( aPhoneticText, aPortions, aBaseText.getLength() );
422     }
423 }
424 
425 void RichString::importCharArray( BiffInputStream& rStrm, sal_uInt16 nChars, rtl_TextEncoding eTextEnc )
426 {
427     createPortion()->setText( rStrm.readCharArrayUC( nChars, eTextEnc ) );
428 }
429 
430 void RichString::importByteString( BiffInputStream& rStrm, rtl_TextEncoding eTextEnc, BiffStringFlags nFlags )
431 {
432     OSL_ENSURE( !getFlag( nFlags, BIFF_STR_KEEPFONTS ), "RichString::importString - keep fonts not implemented" );
433     OSL_ENSURE( !getFlag( nFlags, static_cast< BiffStringFlags >( ~(BIFF_STR_8BITLENGTH | BIFF_STR_EXTRAFONTS) ) ), "RichString::importByteString - unknown flag" );
434     bool b8BitLength = getFlag( nFlags, BIFF_STR_8BITLENGTH );
435 
436     OString aBaseText = rStrm.readByteString( !b8BitLength );
437 
438     if( !rStrm.isEof() && getFlag( nFlags, BIFF_STR_EXTRAFONTS ) )
439     {
440         FontPortionModelList aPortions;
441         aPortions.importPortions( rStrm, false );
442         createTextPortions( aBaseText, eTextEnc, aPortions );
443     }
444     else
445     {
446         createPortion()->setText( OStringToOUString( aBaseText, eTextEnc ) );
447     }
448 }
449 
450 void RichString::importUniString( BiffInputStream& rStrm, BiffStringFlags nFlags )
451 {
452     OSL_ENSURE( !getFlag( nFlags, BIFF_STR_KEEPFONTS ), "RichString::importUniString - keep fonts not implemented" );
453     OSL_ENSURE( !getFlag( nFlags, static_cast< BiffStringFlags >( ~(BIFF_STR_8BITLENGTH | BIFF_STR_SMARTFLAGS) ) ), "RichString::importUniString - unknown flag" );
454     bool b8BitLength = getFlag( nFlags, BIFF_STR_8BITLENGTH );
455 
456     // --- string header ---
457     sal_uInt16 nChars = b8BitLength ? rStrm.readuInt8() : rStrm.readuInt16();
458     sal_uInt8 nFlagField = 0;
459     if( (nChars > 0) || !getFlag( nFlags, BIFF_STR_SMARTFLAGS ) )
460         rStrm >> nFlagField;
461     bool b16Bit    = getFlag( nFlagField, BIFF_STRF_16BIT );
462     bool bFonts    = getFlag( nFlagField, BIFF_STRF_RICH );
463     bool bPhonetic = getFlag( nFlagField, BIFF_STRF_PHONETIC );
464     sal_uInt16 nFontCount = bFonts ? rStrm.readuInt16() : 0;
465     sal_Int32 nPhoneticSize = bPhonetic ? rStrm.readInt32() : 0;
466 
467     // --- character array ---
468     OUString aBaseText = rStrm.readUniStringChars( nChars, b16Bit );
469 
470     // --- formatting ---
471     // #122185# bRich flag may be set, but format runs may be missing
472     if( !rStrm.isEof() && (nFontCount > 0) )
473     {
474         FontPortionModelList aPortions;
475         aPortions.importPortions( rStrm, nFontCount, BIFF_FONTPORTION_16BIT );
476         createTextPortions( aBaseText, aPortions );
477     }
478     else
479     {
480         createPortion()->setText( aBaseText );
481     }
482 
483     // --- Asian phonetic information ---
484     // #122185# bPhonetic flag may be set, but phonetic info may be missing
485     if( !rStrm.isEof() && (nPhoneticSize > 0) )
486     {
487         sal_Int64 nPhoneticEnd = rStrm.tell() + nPhoneticSize;
488         OSL_ENSURE( nPhoneticSize > 14, "RichString::importUniString - wrong size of phonetic data" );
489         if( nPhoneticSize > 14 )
490         {
491             sal_uInt16 nId, nSize;
492             rStrm >> nId >> nSize;
493             OSL_ENSURE( nId == 1, "RichString::importUniString - unknown phonetic data identifier" );
494             sal_Int32 nMinSize = nSize + 4;
495             OSL_ENSURE( nMinSize <= nPhoneticSize, "RichString::importUniString - wrong size of phonetic data" );
496             if( (nId == 1) && (nMinSize <= nPhoneticSize) )
497             {
498                 maPhonSettings.importStringData( rStrm );
499                 PhoneticPortionModelList aPortions;
500                 OUString aPhoneticText = aPortions.importPortions( rStrm, nPhoneticSize );
501                 createPhoneticPortions( aPhoneticText, aPortions, aBaseText.getLength() );
502             }
503         }
504         rStrm.seek( nPhoneticEnd );
505     }
506 }
507 
508 void RichString::finalizeImport()
509 {
510     maTextPortions.forEachMem( &RichStringPortion::finalizeImport );
511 }
512 
513 bool RichString::extractPlainString( OUString& orString, const Font* pFirstPortionFont ) const
514 {
515     if( !maPhonPortions.empty() )
516         return false;
517     if( maTextPortions.empty() )
518     {
519         orString = OUString();
520         return true;
521     }
522     if( (maTextPortions.size() == 1) && !maTextPortions.front()->hasFont() && !lclNeedsRichTextFormat( pFirstPortionFont ) )
523     {
524         orString = maTextPortions.front()->getText();
525         return true;
526     }
527     return false;
528 }
529 
530 void RichString::convert( const Reference< XText >& rxText, const Font* pFirstPortionFont ) const
531 {
532     bool bReplace = true;
533     for( PortionVector::const_iterator aIt = maTextPortions.begin(), aEnd = maTextPortions.end(); aIt != aEnd; ++aIt )
534     {
535         (*aIt)->convert( rxText, pFirstPortionFont, bReplace );
536         pFirstPortionFont = 0;  // use passed font for first portion only
537         bReplace = false;       // do not replace first portion text with following portions
538     }
539 }
540 
541 // private --------------------------------------------------------------------
542 
543 RichStringPortionRef RichString::createPortion()
544 {
545     RichStringPortionRef xPortion( new RichStringPortion( *this ) );
546     maTextPortions.push_back( xPortion );
547     return xPortion;
548 }
549 
550 RichStringPhoneticRef RichString::createPhonetic()
551 {
552     RichStringPhoneticRef xPhonetic( new RichStringPhonetic( *this ) );
553     maPhonPortions.push_back( xPhonetic );
554     return xPhonetic;
555 }
556 
557 void RichString::createTextPortions( const OString& rText, rtl_TextEncoding eTextEnc, FontPortionModelList& rPortions )
558 {
559     maTextPortions.clear();
560     sal_Int32 nStrLen = rText.getLength();
561     if( nStrLen > 0 )
562     {
563         // add leading and trailing string position to ease the following loop
564         if( rPortions.empty() || (rPortions.front().mnPos > 0) )
565             rPortions.insert( rPortions.begin(), FontPortionModel( 0, -1 ) );
566         if( rPortions.back().mnPos < nStrLen )
567             rPortions.push_back( FontPortionModel( nStrLen, -1 ) );
568 
569         // create all string portions according to the font id vector
570         for( FontPortionModelList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
571         {
572             sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
573             if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
574             {
575                 // convert byte string to unicode string, using current font encoding
576                 FontRef xFont = getStyles().getFont( aIt->mnFontId );
577                 rtl_TextEncoding eFontEnc = xFont.get() ? xFont->getFontEncoding() : eTextEnc;
578                 OUString aUniStr = OStringToOUString( rText.copy( aIt->mnPos, nPortionLen ), eFontEnc );
579                 // create string portion
580                 RichStringPortionRef xPortion = createPortion();
581                 xPortion->setText( aUniStr );
582                 xPortion->setFontId( aIt->mnFontId );
583             }
584         }
585     }
586 }
587 
588 void RichString::createTextPortions( const OUString& rText, FontPortionModelList& rPortions )
589 {
590     maTextPortions.clear();
591     sal_Int32 nStrLen = rText.getLength();
592     if( nStrLen > 0 )
593     {
594         // add leading and trailing string position to ease the following loop
595         if( rPortions.empty() || (rPortions.front().mnPos > 0) )
596             rPortions.insert( rPortions.begin(), FontPortionModel( 0, -1 ) );
597         if( rPortions.back().mnPos < nStrLen )
598             rPortions.push_back( FontPortionModel( nStrLen, -1 ) );
599 
600         // create all string portions according to the font id vector
601         for( FontPortionModelList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
602         {
603             sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
604             if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
605             {
606                 RichStringPortionRef xPortion = createPortion();
607                 xPortion->setText( rText.copy( aIt->mnPos, nPortionLen ) );
608                 xPortion->setFontId( aIt->mnFontId );
609             }
610         }
611     }
612 }
613 
614 void RichString::createPhoneticPortions( const ::rtl::OUString& rText, PhoneticPortionModelList& rPortions, sal_Int32 nBaseLen )
615 {
616     maPhonPortions.clear();
617     sal_Int32 nStrLen = rText.getLength();
618     if( nStrLen > 0 )
619     {
620         // no portions - assign phonetic text to entire base text
621         if( rPortions.empty() )
622             rPortions.push_back( PhoneticPortionModel( 0, 0, nBaseLen ) );
623         // add trailing string position to ease the following loop
624         if( rPortions.back().mnPos < nStrLen )
625             rPortions.push_back( PhoneticPortionModel( nStrLen, nBaseLen, 0 ) );
626 
627         // create all phonetic portions according to the portions vector
628         for( PhoneticPortionModelList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
629         {
630             sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
631             if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
632             {
633                 RichStringPhoneticRef xPhonetic = createPhonetic();
634                 xPhonetic->setText( rText.copy( aIt->mnPos, nPortionLen ) );
635                 xPhonetic->setBaseRange( aIt->mnBasePos, aIt->mnBasePos + aIt->mnBaseLen );
636             }
637         }
638     }
639 }
640 
641 // ============================================================================
642 
643 } // namespace xls
644 } // namespace oox
645