Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : : #include "richstring.hxx"
30 : :
31 : : #include <com/sun/star/text/XText.hpp>
32 : : #include <rtl/ustrbuf.hxx>
33 : : #include "oox/helper/attributelist.hxx"
34 : : #include "oox/helper/propertyset.hxx"
35 : : #include "biffinputstream.hxx"
36 : :
37 : : namespace oox {
38 : : namespace xls {
39 : :
40 : : // ============================================================================
41 : :
42 : : using namespace ::com::sun::star::text;
43 : : using namespace ::com::sun::star::uno;
44 : :
45 : : using ::rtl::OString;
46 : : using ::rtl::OUString;
47 : : using ::rtl::OUStringBuffer;
48 : :
49 : : // ============================================================================
50 : :
51 : : namespace {
52 : :
53 : : const sal_uInt8 BIFF12_STRINGFLAG_FONTS = 0x01;
54 : : const sal_uInt8 BIFF12_STRINGFLAG_PHONETICS = 0x02;
55 : :
56 : 150 : inline bool lclNeedsRichTextFormat( const Font* pFont )
57 : : {
58 [ + + ][ - + ]: 150 : return pFont && pFont->needsRichTextFormat();
59 : : }
60 : :
61 : : } // namespace
62 : :
63 : : // ============================================================================
64 : :
65 : 132 : RichStringPortion::RichStringPortion( const WorkbookHelper& rHelper ) :
66 : : WorkbookHelper( rHelper ),
67 [ + - ]: 132 : mnFontId( -1 )
68 : : {
69 : 132 : }
70 : :
71 : 132 : void RichStringPortion::setText( const OUString& rText )
72 : : {
73 : 132 : maText = rText;
74 : 132 : }
75 : :
76 : 3 : FontRef RichStringPortion::createFont()
77 : : {
78 [ + - ]: 3 : mxFont.reset( new Font( *this, false ) );
79 : 3 : return mxFont;
80 : : }
81 : :
82 : 0 : void RichStringPortion::setFontId( sal_Int32 nFontId )
83 : : {
84 : 0 : mnFontId = nFontId;
85 : 0 : }
86 : :
87 : 132 : void RichStringPortion::finalizeImport()
88 : : {
89 [ + + ]: 132 : if( mxFont.get() )
90 : 3 : mxFont->finalizeImport();
91 [ - + ]: 129 : else if( mnFontId >= 0 )
92 [ # # ]: 0 : mxFont = getStyles().getFont( mnFontId );
93 : 132 : }
94 : :
95 : 0 : void RichStringPortion::convert( const Reference< XText >& rxText, const Font* pFont, bool bReplace )
96 : : {
97 : 0 : Reference< XTextRange > xRange;
98 [ # # ]: 0 : if( bReplace )
99 [ # # ]: 0 : xRange.set( rxText, UNO_QUERY );
100 : : else
101 [ # # ][ # # ]: 0 : xRange = rxText->getEnd();
[ # # ]
102 : : OSL_ENSURE( xRange.is(), "RichStringPortion::convert - cannot get text range interface" );
103 : :
104 [ # # ]: 0 : if( xRange.is() )
105 : : {
106 [ # # ][ # # ]: 0 : xRange->setString( maText );
107 [ # # ]: 0 : if( mxFont.get() )
108 : : {
109 [ # # ]: 0 : PropertySet aPropSet( xRange );
110 [ # # ][ # # ]: 0 : mxFont->writeToPropertySet( aPropSet, FONT_PROPTYPE_TEXT );
111 : : }
112 : :
113 : : /* Some font attributes cannot be set to cell formatting in Calc but
114 : : require to use rich formatting, e.g. font escapement. But do not
115 : : use the passed font if this portion has its own font. */
116 [ # # ][ # # ]: 0 : else if( lclNeedsRichTextFormat( pFont ) )
117 : : {
118 [ # # ]: 0 : PropertySet aPropSet( xRange );
119 [ # # ][ # # ]: 0 : pFont->writeToPropertySet( aPropSet, FONT_PROPTYPE_TEXT );
120 : : }
121 : 0 : }
122 : 0 : }
123 : :
124 : 3 : void RichStringPortion::writeFontProperties( const Reference<XText>& rxText, const Font* pFont ) const
125 : : {
126 [ + - ]: 3 : PropertySet aPropSet(rxText);
127 : :
128 [ + - ]: 3 : if (mxFont.get())
129 [ + - ]: 3 : mxFont->writeToPropertySet(aPropSet, FONT_PROPTYPE_TEXT);
130 : :
131 [ + - ][ - + ]: 3 : if (lclNeedsRichTextFormat(pFont))
132 [ # # ][ + - ]: 3 : pFont->writeToPropertySet(aPropSet, FONT_PROPTYPE_TEXT);
133 : 3 : }
134 : :
135 : : // ----------------------------------------------------------------------------
136 : :
137 : 0 : void FontPortionModel::read( SequenceInputStream& rStrm )
138 : : {
139 : 0 : mnPos = rStrm.readuInt16();
140 : 0 : mnFontId = rStrm.readuInt16();
141 : 0 : }
142 : :
143 : : // ----------------------------------------------------------------------------
144 : :
145 : 0 : void FontPortionModelList::appendPortion( const FontPortionModel& rPortion )
146 : : {
147 : : // #i33341# real life -- same character index may occur several times
148 : : OSL_ENSURE( empty() || (back().mnPos <= rPortion.mnPos), "FontPortionModelList::appendPortion - wrong char order" );
149 [ # # ][ # # ]: 0 : if( empty() || (back().mnPos < rPortion.mnPos) )
[ # # ]
150 : 0 : push_back( rPortion );
151 : : else
152 : 0 : back().mnFontId = rPortion.mnFontId;
153 : 0 : }
154 : :
155 : 0 : void FontPortionModelList::importPortions( SequenceInputStream& rStrm )
156 : : {
157 : 0 : sal_Int32 nCount = rStrm.readInt32();
158 : 0 : clear();
159 [ # # ]: 0 : if( nCount > 0 )
160 : : {
161 [ # # ][ # # ]: 0 : reserve( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 4 ) );
[ # # ]
162 : : /* #i33341# real life -- same character index may occur several times
163 : : -> use appendPortion() to validate string position. */
164 : 0 : FontPortionModel aPortion;
165 [ # # ][ # # ]: 0 : for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
[ # # ]
166 : : {
167 [ # # ]: 0 : aPortion.read( rStrm );
168 [ # # ]: 0 : appendPortion( aPortion );
169 : : }
170 : : }
171 : 0 : }
172 : :
173 : : // ============================================================================
174 : :
175 : 192 : PhoneticDataModel::PhoneticDataModel() :
176 : : mnFontId( -1 ),
177 : : mnType( XML_fullwidthKatakana ),
178 : 192 : mnAlignment( XML_left )
179 : : {
180 : 192 : }
181 : :
182 : 0 : void PhoneticDataModel::setBiffData( sal_Int32 nType, sal_Int32 nAlignment )
183 : : {
184 : : static const sal_Int32 spnTypeIds[] = { XML_halfwidthKatakana, XML_fullwidthKatakana, XML_hiragana, XML_noConversion };
185 [ # # ]: 0 : mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_fullwidthKatakana );
186 : :
187 : : static const sal_Int32 spnAlignments[] = { XML_noControl, XML_left, XML_center, XML_distributed };
188 [ # # ]: 0 : mnAlignment = STATIC_ARRAY_SELECT( spnAlignments, nAlignment, XML_left );
189 : 0 : }
190 : :
191 : : // ----------------------------------------------------------------------------
192 : :
193 : 192 : PhoneticSettings::PhoneticSettings( const WorkbookHelper& rHelper ) :
194 : 192 : WorkbookHelper( rHelper )
195 : : {
196 : 192 : }
197 : :
198 : 0 : void PhoneticSettings::importPhoneticPr( const AttributeList& rAttribs )
199 : : {
200 : 0 : maModel.mnFontId = rAttribs.getInteger( XML_fontId, -1 );
201 : 0 : maModel.mnType = rAttribs.getToken( XML_type, XML_fullwidthKatakana );
202 : 0 : maModel.mnAlignment = rAttribs.getToken( XML_alignment, XML_left );
203 : 0 : }
204 : :
205 : 0 : void PhoneticSettings::importPhoneticPr( SequenceInputStream& rStrm )
206 : : {
207 : : sal_uInt16 nFontId;
208 : : sal_Int32 nType, nAlignment;
209 [ # # ][ # # ]: 0 : rStrm >> nFontId >> nType >> nAlignment;
[ # # ]
210 : 0 : maModel.mnFontId = nFontId;
211 : 0 : maModel.setBiffData( nType, nAlignment );
212 : 0 : }
213 : :
214 : 0 : void PhoneticSettings::importStringData( SequenceInputStream& rStrm )
215 : : {
216 : : sal_uInt16 nFontId, nFlags;
217 [ # # ][ # # ]: 0 : rStrm >> nFontId >> nFlags;
218 : 0 : maModel.mnFontId = nFontId;
219 : 0 : maModel.setBiffData( extractValue< sal_Int32 >( nFlags, 0, 2 ), extractValue< sal_Int32 >( nFlags, 2, 2 ) );
220 : 0 : }
221 : :
222 : : // ============================================================================
223 : :
224 : 0 : RichStringPhonetic::RichStringPhonetic( const WorkbookHelper& rHelper ) :
225 : : WorkbookHelper( rHelper ),
226 : : mnBasePos( -1 ),
227 : 0 : mnBaseEnd( -1 )
228 : : {
229 : 0 : }
230 : :
231 : 0 : void RichStringPhonetic::setText( const OUString& rText )
232 : : {
233 : 0 : maText = rText;
234 : 0 : }
235 : :
236 : 0 : void RichStringPhonetic::importPhoneticRun( const AttributeList& rAttribs )
237 : : {
238 : 0 : mnBasePos = rAttribs.getInteger( XML_sb, -1 );
239 : 0 : mnBaseEnd = rAttribs.getInteger( XML_eb, -1 );
240 : 0 : }
241 : :
242 : 0 : void RichStringPhonetic::setBaseRange( sal_Int32 nBasePos, sal_Int32 nBaseEnd )
243 : : {
244 : 0 : mnBasePos = nBasePos;
245 : 0 : mnBaseEnd = nBaseEnd;
246 : 0 : }
247 : :
248 : : // ----------------------------------------------------------------------------
249 : :
250 : 0 : void PhoneticPortionModel::read( SequenceInputStream& rStrm )
251 : : {
252 : 0 : mnPos = rStrm.readuInt16();
253 : 0 : mnBasePos = rStrm.readuInt16();
254 : 0 : mnBaseLen = rStrm.readuInt16();
255 : 0 : }
256 : :
257 : : // ----------------------------------------------------------------------------
258 : :
259 : 0 : void PhoneticPortionModelList::appendPortion( const PhoneticPortionModel& rPortion )
260 : : {
261 : : // same character index may occur several times
262 : : OSL_ENSURE( empty() || ((back().mnPos <= rPortion.mnPos) &&
263 : : (back().mnBasePos + back().mnBaseLen <= rPortion.mnBasePos)),
264 : : "PhoneticPortionModelList::appendPortion - wrong char order" );
265 [ # # ][ # # ]: 0 : if( empty() || (back().mnPos < rPortion.mnPos) )
[ # # ]
266 : : {
267 : 0 : push_back( rPortion );
268 : : }
269 [ # # ]: 0 : else if( back().mnPos == rPortion.mnPos )
270 : : {
271 : 0 : back().mnBasePos = rPortion.mnBasePos;
272 : 0 : back().mnBaseLen = rPortion.mnBaseLen;
273 : : }
274 : 0 : }
275 : :
276 : 0 : void PhoneticPortionModelList::importPortions( SequenceInputStream& rStrm )
277 : : {
278 : 0 : sal_Int32 nCount = rStrm.readInt32();
279 : 0 : clear();
280 [ # # ]: 0 : if( nCount > 0 )
281 : : {
282 [ # # ][ # # ]: 0 : reserve( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 6 ) );
[ # # ]
283 : 0 : PhoneticPortionModel aPortion;
284 [ # # ][ # # ]: 0 : for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
[ # # ]
285 : : {
286 [ # # ]: 0 : aPortion.read( rStrm );
287 [ # # ]: 0 : appendPortion( aPortion );
288 : : }
289 : : }
290 : 0 : }
291 : :
292 : : // ============================================================================
293 : :
294 : 132 : RichString::RichString( const WorkbookHelper& rHelper ) :
295 : : WorkbookHelper( rHelper ),
296 [ + - ][ + - ]: 132 : maPhonSettings( rHelper )
[ + - ]
297 : : {
298 : 132 : }
299 : :
300 : 129 : RichStringPortionRef RichString::importText( const AttributeList& )
301 : : {
302 : 129 : return createPortion();
303 : : }
304 : :
305 : 3 : RichStringPortionRef RichString::importRun( const AttributeList& )
306 : : {
307 : 3 : return createPortion();
308 : : }
309 : :
310 : 0 : RichStringPhoneticRef RichString::importPhoneticRun( const AttributeList& rAttribs )
311 : : {
312 : 0 : RichStringPhoneticRef xPhonetic = createPhonetic();
313 [ # # ]: 0 : xPhonetic->importPhoneticRun( rAttribs );
314 : 0 : return xPhonetic;
315 : : }
316 : :
317 : 0 : void RichString::importPhoneticPr( const AttributeList& rAttribs )
318 : : {
319 : 0 : maPhonSettings.importPhoneticPr( rAttribs );
320 : 0 : }
321 : :
322 : 0 : void RichString::importString( SequenceInputStream& rStrm, bool bRich )
323 : : {
324 [ # # ][ # # ]: 0 : sal_uInt8 nFlags = bRich ? rStrm.readuInt8() : 0;
325 [ # # ]: 0 : OUString aBaseText = BiffHelper::readString( rStrm );
326 : :
327 [ # # ][ # # ]: 0 : if( !rStrm.isEof() && getFlag( nFlags, BIFF12_STRINGFLAG_FONTS ) )
[ # # ]
328 : : {
329 [ # # ]: 0 : FontPortionModelList aPortions;
330 [ # # ]: 0 : aPortions.importPortions( rStrm );
331 [ # # ]: 0 : createTextPortions( aBaseText, aPortions );
332 : : }
333 : : else
334 : : {
335 [ # # ][ # # ]: 0 : createPortion()->setText( aBaseText );
336 : : }
337 : :
338 [ # # ][ # # ]: 0 : if( !rStrm.isEof() && getFlag( nFlags, BIFF12_STRINGFLAG_PHONETICS ) )
[ # # ]
339 : : {
340 [ # # ]: 0 : OUString aPhoneticText = BiffHelper::readString( rStrm );
341 [ # # ]: 0 : PhoneticPortionModelList aPortions;
342 [ # # ]: 0 : aPortions.importPortions( rStrm );
343 [ # # ]: 0 : maPhonSettings.importStringData( rStrm );
344 [ # # ]: 0 : createPhoneticPortions( aPhoneticText, aPortions, aBaseText.getLength() );
345 : 0 : }
346 : 0 : }
347 : :
348 : 132 : void RichString::finalizeImport()
349 : : {
350 : 132 : maTextPortions.forEachMem( &RichStringPortion::finalizeImport );
351 : 132 : }
352 : :
353 : 147 : bool RichString::extractPlainString( OUString& orString, const Font* pFirstPortionFont ) const
354 : : {
355 [ - + ]: 147 : if( !maPhonPortions.empty() )
356 : 0 : return false;
357 [ - + ]: 147 : if( maTextPortions.empty() )
358 : : {
359 : 0 : orString = OUString();
360 : 0 : return true;
361 : : }
362 [ + - ][ + - ]: 147 : if( (maTextPortions.size() == 1) && !maTextPortions.front()->hasFont() && !lclNeedsRichTextFormat( pFirstPortionFont ) )
[ + - ][ + - ]
363 : : {
364 : 147 : orString = maTextPortions.front()->getText();
365 : 147 : return orString.indexOf( '\x0A' ) < 0;
366 : : }
367 : 147 : return false;
368 : : }
369 : :
370 : 3 : void RichString::convert( const Reference< XText >& rxText, bool bReplaceOld, const Font* pFirstPortionFont ) const
371 : : {
372 [ + - ]: 3 : if (maTextPortions.size() == 1)
373 : : {
374 : : // Set text directly to the cell when the string has only one portion.
375 : : // It's much faster this way.
376 : 3 : RichStringPortion& rPtn = *maTextPortions.front();
377 : 3 : rxText->setString(rPtn.getText());
378 : 3 : rPtn.writeFontProperties(rxText, pFirstPortionFont);
379 : 3 : return;
380 : : }
381 : :
382 [ # # ][ # # ]: 0 : for( PortionVector::const_iterator aIt = maTextPortions.begin(), aEnd = maTextPortions.end(); aIt != aEnd; ++aIt )
383 : : {
384 [ # # ]: 0 : (*aIt)->convert( rxText, pFirstPortionFont, bReplaceOld );
385 : 0 : pFirstPortionFont = 0; // use passed font for first portion only
386 : 0 : bReplaceOld = false; // do not replace first portion text with following portions
387 : : }
388 : : }
389 : :
390 : : // private --------------------------------------------------------------------
391 : :
392 : 132 : RichStringPortionRef RichString::createPortion()
393 : : {
394 [ + - ][ + - ]: 132 : RichStringPortionRef xPortion( new RichStringPortion( *this ) );
395 [ + - ]: 132 : maTextPortions.push_back( xPortion );
396 : 132 : return xPortion;
397 : : }
398 : :
399 : 0 : RichStringPhoneticRef RichString::createPhonetic()
400 : : {
401 [ # # ][ # # ]: 0 : RichStringPhoneticRef xPhonetic( new RichStringPhonetic( *this ) );
402 [ # # ]: 0 : maPhonPortions.push_back( xPhonetic );
403 : 0 : return xPhonetic;
404 : : }
405 : :
406 : 0 : void RichString::createTextPortions( const OUString& rText, FontPortionModelList& rPortions )
407 : : {
408 : 0 : maTextPortions.clear();
409 [ # # ]: 0 : if( !rText.isEmpty() )
410 : : {
411 : 0 : sal_Int32 nStrLen = rText.getLength();
412 : : // add leading and trailing string position to ease the following loop
413 [ # # ][ # # ]: 0 : if( rPortions.empty() || (rPortions.front().mnPos > 0) )
[ # # ]
414 [ # # ]: 0 : rPortions.insert( rPortions.begin(), FontPortionModel( 0, -1 ) );
415 [ # # ]: 0 : if( rPortions.back().mnPos < nStrLen )
416 [ # # ]: 0 : rPortions.push_back( FontPortionModel( nStrLen, -1 ) );
417 : :
418 : : // create all string portions according to the font id vector
419 [ # # ][ # # ]: 0 : for( FontPortionModelList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
420 : : {
421 [ # # ]: 0 : sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
422 [ # # ][ # # ]: 0 : if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
[ # # ]
423 : : {
424 [ # # ]: 0 : RichStringPortionRef xPortion = createPortion();
425 : 0 : xPortion->setText( rText.copy( aIt->mnPos, nPortionLen ) );
426 [ # # ]: 0 : xPortion->setFontId( aIt->mnFontId );
427 : : }
428 : : }
429 : : }
430 : 0 : }
431 : :
432 : 0 : void RichString::createPhoneticPortions( const ::rtl::OUString& rText, PhoneticPortionModelList& rPortions, sal_Int32 nBaseLen )
433 : : {
434 : 0 : maPhonPortions.clear();
435 [ # # ]: 0 : if( !rText.isEmpty())
436 : : {
437 : 0 : sal_Int32 nStrLen = rText.getLength();
438 : : // no portions - assign phonetic text to entire base text
439 [ # # ]: 0 : if( rPortions.empty() )
440 [ # # ]: 0 : rPortions.push_back( PhoneticPortionModel( 0, 0, nBaseLen ) );
441 : : // add trailing string position to ease the following loop
442 [ # # ]: 0 : if( rPortions.back().mnPos < nStrLen )
443 [ # # ]: 0 : rPortions.push_back( PhoneticPortionModel( nStrLen, nBaseLen, 0 ) );
444 : :
445 : : // create all phonetic portions according to the portions vector
446 [ # # ][ # # ]: 0 : for( PhoneticPortionModelList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
447 : : {
448 [ # # ]: 0 : sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
449 [ # # ][ # # ]: 0 : if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
[ # # ]
450 : : {
451 [ # # ]: 0 : RichStringPhoneticRef xPhonetic = createPhonetic();
452 : 0 : xPhonetic->setText( rText.copy( aIt->mnPos, nPortionLen ) );
453 [ # # ]: 0 : xPhonetic->setBaseRange( aIt->mnBasePos, aIt->mnBasePos + aIt->mnBaseLen );
454 : : }
455 : : }
456 : : }
457 : 0 : }
458 : :
459 : : // ============================================================================
460 : :
461 : : } // namespace xls
462 [ + - ][ + - ]: 24 : } // namespace oox
463 : :
464 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|