xref: /aoo42x/main/oox/source/xls/richstring.cxx (revision 0dac23a0)
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         /*  Some font attributes cannot be set to cell formatting in Calc but
112             require to use rich formatting, e.g. font escapement. But do not
113             use the passed font if this portion has its own font. */
114         else 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 orString.indexOf( '\x0A' ) < 0;
526     }
527     return false;
528 }
529 
530 void RichString::convert( const Reference< XText >& rxText, bool bReplaceOld, const Font* pFirstPortionFont ) const
531 {
532     for( PortionVector::const_iterator aIt = maTextPortions.begin(), aEnd = maTextPortions.end(); aIt != aEnd; ++aIt )
533     {
534         (*aIt)->convert( rxText, pFirstPortionFont, bReplaceOld );
535         pFirstPortionFont = 0;  // use passed font for first portion only
536         bReplaceOld = false;    // do not replace first portion text with following portions
537     }
538 }
539 
540 // private --------------------------------------------------------------------
541 
542 RichStringPortionRef RichString::createPortion()
543 {
544     RichStringPortionRef xPortion( new RichStringPortion( *this ) );
545     maTextPortions.push_back( xPortion );
546     return xPortion;
547 }
548 
549 RichStringPhoneticRef RichString::createPhonetic()
550 {
551     RichStringPhoneticRef xPhonetic( new RichStringPhonetic( *this ) );
552     maPhonPortions.push_back( xPhonetic );
553     return xPhonetic;
554 }
555 
556 void RichString::createTextPortions( const OString& rText, rtl_TextEncoding eTextEnc, FontPortionModelList& rPortions )
557 {
558     maTextPortions.clear();
559     sal_Int32 nStrLen = rText.getLength();
560     if( nStrLen > 0 )
561     {
562         // add leading and trailing string position to ease the following loop
563         if( rPortions.empty() || (rPortions.front().mnPos > 0) )
564             rPortions.insert( rPortions.begin(), FontPortionModel( 0, -1 ) );
565         if( rPortions.back().mnPos < nStrLen )
566             rPortions.push_back( FontPortionModel( nStrLen, -1 ) );
567 
568         // create all string portions according to the font id vector
569         for( FontPortionModelList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
570         {
571             sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
572             if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
573             {
574                 // convert byte string to unicode string, using current font encoding
575                 FontRef xFont = getStyles().getFont( aIt->mnFontId );
576                 rtl_TextEncoding eFontEnc = xFont.get() ? xFont->getFontEncoding() : eTextEnc;
577                 OUString aUniStr = OStringToOUString( rText.copy( aIt->mnPos, nPortionLen ), eFontEnc );
578                 // create string portion
579                 RichStringPortionRef xPortion = createPortion();
580                 xPortion->setText( aUniStr );
581                 xPortion->setFontId( aIt->mnFontId );
582             }
583         }
584     }
585 }
586 
587 void RichString::createTextPortions( const OUString& rText, FontPortionModelList& rPortions )
588 {
589     maTextPortions.clear();
590     sal_Int32 nStrLen = rText.getLength();
591     if( nStrLen > 0 )
592     {
593         // add leading and trailing string position to ease the following loop
594         if( rPortions.empty() || (rPortions.front().mnPos > 0) )
595             rPortions.insert( rPortions.begin(), FontPortionModel( 0, -1 ) );
596         if( rPortions.back().mnPos < nStrLen )
597             rPortions.push_back( FontPortionModel( nStrLen, -1 ) );
598 
599         // create all string portions according to the font id vector
600         for( FontPortionModelList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
601         {
602             sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
603             if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
604             {
605                 RichStringPortionRef xPortion = createPortion();
606                 xPortion->setText( rText.copy( aIt->mnPos, nPortionLen ) );
607                 xPortion->setFontId( aIt->mnFontId );
608             }
609         }
610     }
611 }
612 
613 void RichString::createPhoneticPortions( const ::rtl::OUString& rText, PhoneticPortionModelList& rPortions, sal_Int32 nBaseLen )
614 {
615     maPhonPortions.clear();
616     sal_Int32 nStrLen = rText.getLength();
617     if( nStrLen > 0 )
618     {
619         // no portions - assign phonetic text to entire base text
620         if( rPortions.empty() )
621             rPortions.push_back( PhoneticPortionModel( 0, 0, nBaseLen ) );
622         // add trailing string position to ease the following loop
623         if( rPortions.back().mnPos < nStrLen )
624             rPortions.push_back( PhoneticPortionModel( nStrLen, nBaseLen, 0 ) );
625 
626         // create all phonetic portions according to the portions vector
627         for( PhoneticPortionModelList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
628         {
629             sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
630             if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
631             {
632                 RichStringPhoneticRef xPhonetic = createPhonetic();
633                 xPhonetic->setText( rText.copy( aIt->mnPos, nPortionLen ) );
634                 xPhonetic->setBaseRange( aIt->mnBasePos, aIt->mnBasePos + aIt->mnBaseLen );
635             }
636         }
637     }
638 }
639 
640 // ============================================================================
641 
642 } // namespace xls
643 } // namespace oox
644