Update ooo320-m1
[ooovba.git] / oox / source / xls / richstring.cxx
bloba4f8f137965452b58c0d2343f3b08fe6de38dc39
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"
39 using ::rtl::OString;
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;
46 namespace oox {
47 namespace xls {
49 // ============================================================================
51 namespace {
53 const sal_uInt8 OOBIN_STRINGFLAG_FONTS = 0x01;
54 const sal_uInt8 OOBIN_STRINGFLAG_PHONETICS = 0x02;
56 } // namespace
58 // ============================================================================
60 RichStringPortion::RichStringPortion( const WorkbookHelper& rHelper ) :
61 WorkbookHelper( rHelper ),
62 mnFontId( -1 )
66 void RichStringPortion::setText( const OUString& rText )
68 maText = rText;
71 FontRef RichStringPortion::createFont()
73 mxFont.reset( new Font( *this, false ) );
74 return mxFont;
77 void RichStringPortion::setFontId( sal_Int32 nFontId )
79 mnFontId = nFontId;
82 void RichStringPortion::finalizeImport()
84 if( mxFont.get() )
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 );
94 if( mxFont.get() )
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 )
119 switch( eMode )
121 case BIFF_FONTPORTION_8BIT:
122 mnPos = rStrm.readuInt8();
123 mnFontId = rStrm.readuInt8();
124 break;
125 case BIFF_FONTPORTION_16BIT:
126 mnPos = rStrm.readuInt16();
127 mnFontId = rStrm.readuInt16();
128 break;
129 case BIFF_FONTPORTION_OBJ:
130 mnPos = rStrm.readuInt16();
131 mnFontId = rStrm.readuInt16();
132 rStrm.skip( 4 );
133 break;
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 );
145 else
146 back().mnFontId = rPortion.mnFontId;
149 void FontPortionModelList::importPortions( RecordInputStream& rStrm )
151 sal_Int32 nCount = rStrm.readInt32();
152 clear();
153 if( nCount > 0 )
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 )
169 clear();
170 reserve( nCount );
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() :
190 mnFontId( -1 ),
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 )
221 sal_uInt16 nFontId;
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 ),
257 mnBasePos( -1 ),
258 mnBaseEnd( -1 )
262 void RichStringPhonetic::setText( const OUString& rText )
264 maText = 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();
317 clear();
318 if( nCount > 0 )
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 );
343 clear();
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 );
378 return xPhonetic;
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 );
397 else
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 );
426 else
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 ) )
442 rStrm >> nFlagField;
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 );
460 else
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 );
518 return xPortion;
521 RichStringPhoneticRef RichString::createPhonetic()
523 RichStringPhoneticRef xPhonetic( new RichStringPhonetic( *this ) );
524 maPhonPortions.push_back( xPhonetic );
525 return xPhonetic;
528 void RichString::createFontPortions( const OString& rText, rtl_TextEncoding eDefaultTextEnc, FontPortionModelList& rPortions )
530 maFontPortions.clear();
531 sal_Int32 nStrLen = rText.getLength();
532 if( nStrLen > 0 )
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();
563 if( nStrLen > 0 )
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();
589 if( nStrLen > 0 )
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 // ============================================================================
614 } // namespace xls
615 } // namespace oox