1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: richstring.cxx,v $
10 * $Revision: 1.4.20.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "oox/xls/richstring.hxx"
32 #include <rtl/ustrbuf.hxx>
33 #include <com/sun/star/text/XText.hpp>
34 #include "oox/helper/attributelist.hxx"
35 #include "oox/helper/propertyset.hxx"
36 #include "oox/helper/recordinputstream.hxx"
37 #include "oox/xls/biffinputstream.hxx"
40 using ::rtl::OUString
;
41 using ::rtl::OUStringBuffer
;
42 using ::com::sun::star::uno::Reference
;
43 using ::com::sun::star::text::XText
;
44 using ::com::sun::star::text::XTextRange
;
49 // ============================================================================
53 const sal_uInt8 OOBIN_STRINGFLAG_FONTS
= 0x01;
54 const sal_uInt8 OOBIN_STRINGFLAG_PHONETICS
= 0x02;
58 // ============================================================================
60 RichStringPortion::RichStringPortion( const WorkbookHelper
& rHelper
) :
61 WorkbookHelper( rHelper
),
66 void RichStringPortion::setText( const OUString
& rText
)
71 FontRef
RichStringPortion::createFont()
73 mxFont
.reset( new Font( *this, false ) );
77 void RichStringPortion::setFontId( sal_Int32 nFontId
)
82 void RichStringPortion::finalizeImport()
85 mxFont
->finalizeImport();
86 else if( mnFontId
>= 0 )
87 mxFont
= getStyles().getFont( mnFontId
);
90 void RichStringPortion::convert( const Reference
< XText
>& rxText
, sal_Int32 nXfId
)
92 Reference
< XTextRange
> xRange
= rxText
->getEnd();
93 xRange
->setString( maText
);
96 PropertySet
aPropSet( xRange
);
97 mxFont
->writeToPropertySet( aPropSet
, FONT_PROPTYPE_TEXT
);
99 if( const Font
* pFont
= getStyles().getFontFromCellXf( nXfId
).get() )
101 if( pFont
->needsRichTextFormat() )
103 PropertySet
aPropSet( xRange
);
104 pFont
->writeToPropertySet( aPropSet
, FONT_PROPTYPE_TEXT
);
109 // ----------------------------------------------------------------------------
111 void FontPortionModel::read( RecordInputStream
& rStrm
)
113 mnPos
= rStrm
.readuInt16();
114 mnFontId
= rStrm
.readuInt16();
117 void FontPortionModel::read( BiffInputStream
& rStrm
, BiffFontPortionMode eMode
)
121 case BIFF_FONTPORTION_8BIT
:
122 mnPos
= rStrm
.readuInt8();
123 mnFontId
= rStrm
.readuInt8();
125 case BIFF_FONTPORTION_16BIT
:
126 mnPos
= rStrm
.readuInt16();
127 mnFontId
= rStrm
.readuInt16();
129 case BIFF_FONTPORTION_OBJ
:
130 mnPos
= rStrm
.readuInt16();
131 mnFontId
= rStrm
.readuInt16();
137 // ----------------------------------------------------------------------------
139 void FontPortionModelList::appendPortion( const FontPortionModel
& rPortion
)
141 // #i33341# real life -- same character index may occur several times
142 OSL_ENSURE( empty() || (back().mnPos
<= rPortion
.mnPos
), "FontPortionModelList::appendPortion - wrong char order" );
143 if( empty() || (back().mnPos
< rPortion
.mnPos
) )
144 push_back( rPortion
);
146 back().mnFontId
= rPortion
.mnFontId
;
149 void FontPortionModelList::importPortions( RecordInputStream
& rStrm
)
151 sal_Int32 nCount
= rStrm
.readInt32();
155 reserve( getLimitedValue
< size_t, sal_Int64
>( nCount
, 0, rStrm
.getRemaining() / 4 ) );
156 /* #i33341# real life -- same character index may occur several times
157 -> use appendPortion() to validate string position. */
158 FontPortionModel aPortion
;
159 for( sal_Int32 nIndex
= 0; !rStrm
.isEof() && (nIndex
< nCount
); ++nIndex
)
161 aPortion
.read( rStrm
);
162 appendPortion( aPortion
);
167 void FontPortionModelList::importPortions( BiffInputStream
& rStrm
, sal_uInt16 nCount
, BiffFontPortionMode eMode
)
171 /* #i33341# real life -- same character index may occur several times
172 -> use appendPortion() to validate string position. */
173 FontPortionModel aPortion
;
174 for( sal_uInt16 nIndex
= 0; !rStrm
.isEof() && (nIndex
< nCount
); ++nIndex
)
176 aPortion
.read( rStrm
, eMode
);
177 appendPortion( aPortion
);
181 void FontPortionModelList::importPortions( BiffInputStream
& rStrm
, bool b16Bit
)
183 sal_uInt16 nCount
= b16Bit
? rStrm
.readuInt16() : rStrm
.readuInt8();
184 importPortions( rStrm
, nCount
, b16Bit
? BIFF_FONTPORTION_16BIT
: BIFF_FONTPORTION_8BIT
);
187 // ============================================================================
189 PhoneticDataModel::PhoneticDataModel() :
191 mnType( XML_fullwidthKatakana
),
192 mnAlignment( XML_left
)
196 void PhoneticDataModel::setBinData( sal_Int32 nType
, sal_Int32 nAlignment
)
198 static const sal_Int32 spnTypeIds
[] = { XML_halfwidthKatakana
, XML_fullwidthKatakana
, XML_hiragana
, XML_noConversion
};
199 mnType
= STATIC_ARRAY_SELECT( spnTypeIds
, nType
, XML_fullwidthKatakana
);
201 static const sal_Int32 spnAlignments
[] = { XML_noControl
, XML_left
, XML_center
, XML_distributed
};
202 mnAlignment
= STATIC_ARRAY_SELECT( spnAlignments
, nAlignment
, XML_left
);
205 // ----------------------------------------------------------------------------
207 PhoneticSettings::PhoneticSettings( const WorkbookHelper
& rHelper
) :
208 WorkbookHelper( rHelper
)
212 void PhoneticSettings::importPhoneticPr( const AttributeList
& rAttribs
)
214 maModel
.mnFontId
= rAttribs
.getInteger( XML_fontId
, -1 );
215 maModel
.mnType
= rAttribs
.getToken( XML_type
, XML_fullwidthKatakana
);
216 maModel
.mnAlignment
= rAttribs
.getToken( XML_alignment
, XML_left
);
219 void PhoneticSettings::importPhoneticPr( RecordInputStream
& rStrm
)
222 sal_Int32 nType
, nAlignment
;
223 rStrm
>> nFontId
>> nType
>> nAlignment
;
224 maModel
.mnFontId
= nFontId
;
225 maModel
.setBinData( nType
, nAlignment
);
228 void PhoneticSettings::importPhoneticPr( BiffInputStream
& rStrm
)
230 sal_uInt16 nFontId
, nFlags
;
231 rStrm
>> nFontId
>> nFlags
;
232 maModel
.mnFontId
= nFontId
;
233 maModel
.setBinData( extractValue
< sal_Int32
>( nFlags
, 0, 2 ), extractValue
< sal_Int32
>( nFlags
, 2, 2 ) );
234 // following: range list with cells showing phonetic text
237 void PhoneticSettings::importStringData( RecordInputStream
& rStrm
)
239 sal_uInt16 nFontId
, nFlags
;
240 rStrm
>> nFontId
>> nFlags
;
241 maModel
.mnFontId
= nFontId
;
242 maModel
.setBinData( extractValue
< sal_Int32
>( nFlags
, 0, 2 ), extractValue
< sal_Int32
>( nFlags
, 2, 2 ) );
245 void PhoneticSettings::importStringData( BiffInputStream
& rStrm
)
247 sal_uInt16 nFontId
, nFlags
;
248 rStrm
>> nFontId
>> nFlags
;
249 maModel
.mnFontId
= nFontId
;
250 maModel
.setBinData( extractValue
< sal_Int32
>( nFlags
, 0, 2 ), extractValue
< sal_Int32
>( nFlags
, 2, 2 ) );
253 // ============================================================================
255 RichStringPhonetic::RichStringPhonetic( const WorkbookHelper
& rHelper
) :
256 WorkbookHelper( rHelper
),
262 void RichStringPhonetic::setText( const OUString
& rText
)
267 void RichStringPhonetic::importPhoneticRun( const AttributeList
& rAttribs
)
269 mnBasePos
= rAttribs
.getInteger( XML_sb
, -1 );
270 mnBaseEnd
= rAttribs
.getInteger( XML_eb
, -1 );
273 void RichStringPhonetic::setBaseRange( sal_Int32 nBasePos
, sal_Int32 nBaseEnd
)
275 mnBasePos
= nBasePos
;
276 mnBaseEnd
= nBaseEnd
;
279 // ----------------------------------------------------------------------------
281 void PhoneticPortionModel::read( RecordInputStream
& rStrm
)
283 mnPos
= rStrm
.readuInt16();
284 mnBasePos
= rStrm
.readuInt16();
285 mnBaseLen
= rStrm
.readuInt16();
288 void PhoneticPortionModel::read( BiffInputStream
& rStrm
)
290 mnPos
= rStrm
.readuInt16();
291 mnBasePos
= rStrm
.readuInt16();
292 mnBaseLen
= rStrm
.readuInt16();
295 // ----------------------------------------------------------------------------
297 void PhoneticPortionModelList::appendPortion( const PhoneticPortionModel
& rPortion
)
299 // same character index may occur several times
300 OSL_ENSURE( empty() || ((back().mnPos
<= rPortion
.mnPos
) &&
301 (back().mnBasePos
+ back().mnBaseLen
<= rPortion
.mnBasePos
)),
302 "PhoneticPortionModelList::appendPortion - wrong char order" );
303 if( empty() || (back().mnPos
< rPortion
.mnPos
) )
305 push_back( rPortion
);
307 else if( back().mnPos
== rPortion
.mnPos
)
309 back().mnBasePos
= rPortion
.mnBasePos
;
310 back().mnBaseLen
= rPortion
.mnBaseLen
;
314 void PhoneticPortionModelList::importPortions( RecordInputStream
& rStrm
)
316 sal_Int32 nCount
= rStrm
.readInt32();
320 reserve( getLimitedValue
< size_t, sal_Int64
>( nCount
, 0, rStrm
.getRemaining() / 6 ) );
321 PhoneticPortionModel aPortion
;
322 for( sal_Int32 nIndex
= 0; !rStrm
.isEof() && (nIndex
< nCount
); ++nIndex
)
324 aPortion
.read( rStrm
);
325 appendPortion( aPortion
);
330 OUString
PhoneticPortionModelList::importPortions( BiffInputStream
& rStrm
, sal_Int32 nPhoneticSize
)
332 OUString aPhoneticText
;
333 sal_uInt16 nPortionCount
, nTextLen1
, nTextLen2
;
334 rStrm
>> nPortionCount
>> nTextLen1
>> nTextLen2
;
335 OSL_ENSURE( nTextLen1
== nTextLen2
, "PhoneticPortionModelList::importPortions - wrong phonetic text length" );
336 if( (nTextLen1
== nTextLen2
) && (nTextLen1
> 0) )
338 sal_Int32 nMinSize
= 2 * nTextLen1
+ 6 * nPortionCount
+ 14;
339 OSL_ENSURE( nMinSize
<= nPhoneticSize
, "PhoneticPortionModelList::importPortions - wrong size of phonetic data" );
340 if( nMinSize
<= nPhoneticSize
)
342 aPhoneticText
= rStrm
.readUnicodeArray( nTextLen1
);
344 reserve( nPortionCount
);
345 PhoneticPortionModel aPortion
;
346 for( sal_uInt16 nPortion
= 0; nPortion
< nPortionCount
; ++nPortion
)
348 aPortion
.read( rStrm
);
349 appendPortion( aPortion
);
353 return aPhoneticText
;
356 // ============================================================================
358 RichString::RichString( const WorkbookHelper
& rHelper
) :
359 WorkbookHelper( rHelper
),
360 maPhonSettings( rHelper
)
364 RichStringPortionRef
RichString::importText( const AttributeList
& )
366 return createPortion();
369 RichStringPortionRef
RichString::importRun( const AttributeList
& )
371 return createPortion();
374 RichStringPhoneticRef
RichString::importPhoneticRun( const AttributeList
& rAttribs
)
376 RichStringPhoneticRef xPhonetic
= createPhonetic();
377 xPhonetic
->importPhoneticRun( rAttribs
);
381 void RichString::importPhoneticPr( const AttributeList
& rAttribs
)
383 maPhonSettings
.importPhoneticPr( rAttribs
);
386 void RichString::importString( RecordInputStream
& rStrm
, bool bRich
)
388 sal_uInt8 nFlags
= bRich
? rStrm
.readuInt8() : 0;
389 OUString aBaseText
= rStrm
.readString();
391 if( !rStrm
.isEof() && getFlag( nFlags
, OOBIN_STRINGFLAG_FONTS
) )
393 FontPortionModelList aPortions
;
394 aPortions
.importPortions( rStrm
);
395 createFontPortions( aBaseText
, aPortions
);
399 createPortion()->setText( aBaseText
);
402 if( !rStrm
.isEof() && getFlag( nFlags
, OOBIN_STRINGFLAG_PHONETICS
) )
404 OUString aPhoneticText
= rStrm
.readString();
405 PhoneticPortionModelList aPortions
;
406 aPortions
.importPortions( rStrm
);
407 maPhonSettings
.importStringData( rStrm
);
408 createPhoneticPortions( aPhoneticText
, aPortions
, aBaseText
.getLength() );
412 void RichString::importByteString( BiffInputStream
& rStrm
, rtl_TextEncoding eDefaultTextEnc
, BiffStringFlags nFlags
)
414 OSL_ENSURE( !getFlag( nFlags
, BIFF_STR_KEEPFONTS
), "RichString::importString - keep fonts not implemented" );
415 OSL_ENSURE( !getFlag( nFlags
, static_cast< BiffStringFlags
>( ~(BIFF_STR_8BITLENGTH
| BIFF_STR_EXTRAFONTS
) ) ), "RichString::importByteString - unknown flag" );
416 bool b8BitLength
= getFlag( nFlags
, BIFF_STR_8BITLENGTH
);
418 OString aBaseText
= rStrm
.readByteString( !b8BitLength
);
420 if( !rStrm
.isEof() && getFlag( nFlags
, BIFF_STR_EXTRAFONTS
) )
422 FontPortionModelList aPortions
;
423 aPortions
.importPortions( rStrm
, false );
424 createFontPortions( aBaseText
, eDefaultTextEnc
, aPortions
);
428 createPortion()->setText( OStringToOUString( aBaseText
, eDefaultTextEnc
) );
432 void RichString::importUniString( BiffInputStream
& rStrm
, BiffStringFlags nFlags
)
434 OSL_ENSURE( !getFlag( nFlags
, BIFF_STR_KEEPFONTS
), "RichString::importUniString - keep fonts not implemented" );
435 OSL_ENSURE( !getFlag( nFlags
, static_cast< BiffStringFlags
>( ~(BIFF_STR_8BITLENGTH
| BIFF_STR_SMARTFLAGS
) ) ), "RichString::importUniString - unknown flag" );
436 bool b8BitLength
= getFlag( nFlags
, BIFF_STR_8BITLENGTH
);
438 // --- string header ---
439 sal_uInt16 nChars
= b8BitLength
? rStrm
.readuInt8() : rStrm
.readuInt16();
440 sal_uInt8 nFlagField
= 0;
441 if( (nChars
> 0) || !getFlag( nFlags
, BIFF_STR_SMARTFLAGS
) )
443 bool b16Bit
= getFlag( nFlagField
, BIFF_STRF_16BIT
);
444 bool bFonts
= getFlag( nFlagField
, BIFF_STRF_RICH
);
445 bool bPhonetic
= getFlag( nFlagField
, BIFF_STRF_PHONETIC
);
446 sal_uInt16 nFontCount
= bFonts
? rStrm
.readuInt16() : 0;
447 sal_Int32 nPhoneticSize
= bPhonetic
? rStrm
.readInt32() : 0;
449 // --- character array ---
450 OUString aBaseText
= rStrm
.readUniStringChars( nChars
, b16Bit
);
452 // --- formatting ---
453 // #122185# bRich flag may be set, but format runs may be missing
454 if( !rStrm
.isEof() && (nFontCount
> 0) )
456 FontPortionModelList aPortions
;
457 aPortions
.importPortions( rStrm
, nFontCount
, BIFF_FONTPORTION_16BIT
);
458 createFontPortions( aBaseText
, aPortions
);
462 createPortion()->setText( aBaseText
);
465 // --- Asian phonetic information ---
466 // #122185# bPhonetic flag may be set, but phonetic info may be missing
467 if( !rStrm
.isEof() && (nPhoneticSize
> 0) )
469 sal_Int64 nPhoneticEnd
= rStrm
.tell() + nPhoneticSize
;
470 OSL_ENSURE( nPhoneticSize
> 14, "RichString::importUniString - wrong size of phonetic data" );
471 if( nPhoneticSize
> 14 )
473 sal_uInt16 nId
, nSize
;
474 rStrm
>> nId
>> nSize
;
475 OSL_ENSURE( nId
== 1, "RichString::importUniString - unknown phonetic data identifier" );
476 sal_Int32 nMinSize
= nSize
+ 4;
477 OSL_ENSURE( nMinSize
<= nPhoneticSize
, "RichString::importUniString - wrong size of phonetic data" );
478 if( (nId
== 1) && (nMinSize
<= nPhoneticSize
) )
480 maPhonSettings
.importStringData( rStrm
);
481 PhoneticPortionModelList aPortions
;
482 OUString aPhoneticText
= aPortions
.importPortions( rStrm
, nPhoneticSize
);
483 createPhoneticPortions( aPhoneticText
, aPortions
, aBaseText
.getLength() );
486 rStrm
.seek( nPhoneticEnd
);
490 void RichString::finalizeImport()
492 maFontPortions
.forEachMem( &RichStringPortion::finalizeImport
);
495 OUString
RichString::getPlainText() const
497 OUStringBuffer aBuffer
;
498 for( PortionVec::const_iterator aIt
= maFontPortions
.begin(), aEnd
= maFontPortions
.end(); aIt
!= aEnd
; ++aIt
)
499 aBuffer
.append( (*aIt
)->getText() );
500 return aBuffer
.makeStringAndClear();
503 void RichString::convert( const Reference
< XText
>& rxText
, sal_Int32 nXfId
) const
505 for( PortionVec::const_iterator aIt
= maFontPortions
.begin(), aEnd
= maFontPortions
.end(); aIt
!= aEnd
; ++aIt
)
507 (*aIt
)->convert( rxText
, nXfId
);
508 nXfId
= -1; // use passed XF identifier for first portion only
512 // private --------------------------------------------------------------------
514 RichStringPortionRef
RichString::createPortion()
516 RichStringPortionRef
xPortion( new RichStringPortion( *this ) );
517 maFontPortions
.push_back( xPortion
);
521 RichStringPhoneticRef
RichString::createPhonetic()
523 RichStringPhoneticRef
xPhonetic( new RichStringPhonetic( *this ) );
524 maPhonPortions
.push_back( xPhonetic
);
528 void RichString::createFontPortions( const OString
& rText
, rtl_TextEncoding eDefaultTextEnc
, FontPortionModelList
& rPortions
)
530 maFontPortions
.clear();
531 sal_Int32 nStrLen
= rText
.getLength();
534 // add leading and trailing string position to ease the following loop
535 if( rPortions
.empty() || (rPortions
.front().mnPos
> 0) )
536 rPortions
.insert( rPortions
.begin(), FontPortionModel( 0, -1 ) );
537 if( rPortions
.back().mnPos
< nStrLen
)
538 rPortions
.push_back( FontPortionModel( nStrLen
, -1 ) );
540 // create all string portions according to the font id vector
541 for( FontPortionModelList::const_iterator aIt
= rPortions
.begin(); aIt
->mnPos
< nStrLen
; ++aIt
)
543 sal_Int32 nPortionLen
= (aIt
+ 1)->mnPos
- aIt
->mnPos
;
544 if( (0 < nPortionLen
) && (aIt
->mnPos
+ nPortionLen
<= nStrLen
) )
546 // convert byte string to unicode string, using current font encoding
547 FontRef xFont
= getStyles().getFont( aIt
->mnFontId
);
548 rtl_TextEncoding eTextEnc
= xFont
.get() ? xFont
->getFontEncoding() : eDefaultTextEnc
;
549 OUString aUniStr
= OStringToOUString( rText
.copy( aIt
->mnPos
, nPortionLen
), eTextEnc
);
550 // create string portion
551 RichStringPortionRef xPortion
= createPortion();
552 xPortion
->setText( aUniStr
);
553 xPortion
->setFontId( aIt
->mnFontId
);
559 void RichString::createFontPortions( const OUString
& rText
, FontPortionModelList
& rPortions
)
561 maFontPortions
.clear();
562 sal_Int32 nStrLen
= rText
.getLength();
565 // add leading and trailing string position to ease the following loop
566 if( rPortions
.empty() || (rPortions
.front().mnPos
> 0) )
567 rPortions
.insert( rPortions
.begin(), FontPortionModel( 0, -1 ) );
568 if( rPortions
.back().mnPos
< nStrLen
)
569 rPortions
.push_back( FontPortionModel( nStrLen
, -1 ) );
571 // create all string portions according to the font id vector
572 for( FontPortionModelList::const_iterator aIt
= rPortions
.begin(); aIt
->mnPos
< nStrLen
; ++aIt
)
574 sal_Int32 nPortionLen
= (aIt
+ 1)->mnPos
- aIt
->mnPos
;
575 if( (0 < nPortionLen
) && (aIt
->mnPos
+ nPortionLen
<= nStrLen
) )
577 RichStringPortionRef xPortion
= createPortion();
578 xPortion
->setText( rText
.copy( aIt
->mnPos
, nPortionLen
) );
579 xPortion
->setFontId( aIt
->mnFontId
);
585 void RichString::createPhoneticPortions( const ::rtl::OUString
& rText
, PhoneticPortionModelList
& rPortions
, sal_Int32 nBaseLen
)
587 maPhonPortions
.clear();
588 sal_Int32 nStrLen
= rText
.getLength();
591 // no portions - assign phonetic text to entire base text
592 if( rPortions
.empty() )
593 rPortions
.push_back( PhoneticPortionModel( 0, 0, nBaseLen
) );
594 // add trailing string position to ease the following loop
595 if( rPortions
.back().mnPos
< nStrLen
)
596 rPortions
.push_back( PhoneticPortionModel( nStrLen
, nBaseLen
, 0 ) );
598 // create all phonetic portions according to the portions vector
599 for( PhoneticPortionModelList::const_iterator aIt
= rPortions
.begin(); aIt
->mnPos
< nStrLen
; ++aIt
)
601 sal_Int32 nPortionLen
= (aIt
+ 1)->mnPos
- aIt
->mnPos
;
602 if( (0 < nPortionLen
) && (aIt
->mnPos
+ nPortionLen
<= nStrLen
) )
604 RichStringPhoneticRef xPhonetic
= createPhonetic();
605 xPhonetic
->setText( rText
.copy( aIt
->mnPos
, nPortionLen
) );
606 xPhonetic
->setBaseRange( aIt
->mnBasePos
, aIt
->mnBasePos
+ aIt
->mnBaseLen
);
612 // ============================================================================