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