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