Move setting of LD_LIBRARY_PATH closer to invocation of cppunittester
[LibreOffice.git] / sc / source / filter / oox / richstring.cxx
blob6f2a75b5f7218be5bafc2dfbb81b0dc8d155a0a3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <richstring.hxx>
21 #include <biffhelper.hxx>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <com/sun/star/text/XText.hpp>
25 #include <rtl/ustrbuf.hxx>
26 #include <editeng/editobj.hxx>
27 #include <osl/diagnose.h>
28 #include <oox/helper/binaryinputstream.hxx>
29 #include <oox/helper/attributelist.hxx>
30 #include <oox/helper/propertyset.hxx>
31 #include <oox/token/namespaces.hxx>
32 #include <oox/token/tokens.hxx>
33 #include <editutil.hxx>
35 #include <vcl/svapp.hxx>
37 namespace oox::xls {
39 using namespace ::com::sun::star::text;
40 using namespace ::com::sun::star::uno;
42 namespace {
44 const sal_uInt8 BIFF12_STRINGFLAG_FONTS = 0x01;
45 const sal_uInt8 BIFF12_STRINGFLAG_PHONETICS = 0x02;
47 bool lclNeedsRichTextFormat( const oox::xls::Font* pFont )
49 return pFont && pFont->needsRichTextFormat();
52 } // namespace
54 RichStringPortion::RichStringPortion() :
55 mnFontId( -1 ),
56 mbConverted( false )
60 void RichStringPortion::setText( const OUString& rText )
62 maText = AttributeConversion::decodeXString(rText);
65 FontRef const & RichStringPortion::createFont(const WorkbookHelper& rHelper)
67 mxFont = std::make_shared<Font>( rHelper, false );
68 return mxFont;
71 void RichStringPortion::setFontId( sal_Int32 nFontId )
73 mnFontId = nFontId;
76 void RichStringPortion::finalizeImport(const WorkbookHelper& rHelper)
78 if( mxFont )
79 mxFont->finalizeImport();
80 else if( mnFontId >= 0 )
81 mxFont = rHelper.getStyles().getFont( mnFontId );
84 void RichStringPortion::convert( const Reference< XText >& rxText, bool bReplace )
86 if ( mbConverted )
87 return;
89 Reference< XTextRange > xRange;
90 if( bReplace )
91 xRange = rxText;
92 else
93 xRange = rxText->getEnd();
94 OSL_ENSURE( xRange.is(), "RichStringPortion::convert - cannot get text range interface" );
96 if( xRange.is() )
98 xRange->setString( maText );
99 if( mxFont )
101 PropertySet aPropSet( xRange );
102 mxFont->writeToPropertySet( aPropSet );
106 mbConverted = true;
109 void RichStringPortion::convert( ScEditEngineDefaulter& rEE, ESelection& rSelection, const oox::xls::Font* pFont )
111 rSelection.CollapseToEnd();
112 SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
114 const Font* pFontToUse = mxFont ? mxFont.get() : lclNeedsRichTextFormat( pFont ) ? pFont : nullptr;
116 if ( pFontToUse )
117 pFontToUse->fillToItemSet( aItemSet, true );
119 // #TODO need to manually adjust nEndPos ( and nEndPara ) to cater for any paragraphs
120 sal_Int32 nLastParaLoc = -1;
121 sal_Int32 nSearchIndex = maText.indexOf( '\n' );
122 sal_Int32 nParaOccurrence = 0;
123 while ( nSearchIndex != -1 )
125 nLastParaLoc = nSearchIndex;
126 ++nParaOccurrence;
127 rSelection.end.nIndex = 0;
128 nSearchIndex = maText.indexOf( '\n', nSearchIndex + 1);
131 rSelection.end.nPara += nParaOccurrence;
132 if ( nLastParaLoc != -1 )
134 rSelection.end.nIndex = maText.getLength() - 1 - nLastParaLoc;
136 else
138 rSelection.end.nIndex = rSelection.start.nIndex + maText.getLength();
140 rEE.QuickSetAttribs( aItemSet, rSelection );
143 void RichStringPortion::writeFontProperties( const Reference<XText>& rxText ) const
145 PropertySet aPropSet(rxText);
147 if (mxFont)
148 mxFont->writeToPropertySet(aPropSet);
151 void FontPortionModel::read( SequenceInputStream& rStrm )
153 mnPos = rStrm.readuInt16();
154 mnFontId = rStrm.readuInt16();
157 void FontPortionModelList::appendPortion( const FontPortionModel& rPortion )
159 // #i33341# real life -- same character index may occur several times
160 OSL_ENSURE( mvModels.empty() || (mvModels.back().mnPos <= rPortion.mnPos), "FontPortionModelList::appendPortion - wrong char order" );
161 if( mvModels.empty() || (mvModels.back().mnPos < rPortion.mnPos) )
162 mvModels.push_back( rPortion );
163 else
164 mvModels.back().mnFontId = rPortion.mnFontId;
167 void FontPortionModelList::importPortions( SequenceInputStream& rStrm )
169 sal_Int32 nCount = rStrm.readInt32();
170 mvModels.clear();
171 if( nCount > 0 )
173 mvModels.reserve( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 4 ) );
174 /* #i33341# real life -- same character index may occur several times
175 -> use appendPortion() to validate string position. */
176 FontPortionModel aPortion;
177 for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
179 aPortion.read( rStrm );
180 appendPortion( aPortion );
185 PhoneticDataModel::PhoneticDataModel() :
186 mnFontId( -1 ),
187 mnType( XML_fullwidthKatakana ),
188 mnAlignment( XML_left )
192 void PhoneticDataModel::setBiffData( sal_Int32 nType, sal_Int32 nAlignment )
194 static const sal_Int32 spnTypeIds[] = { XML_halfwidthKatakana, XML_fullwidthKatakana, XML_hiragana, XML_noConversion };
195 mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_fullwidthKatakana );
197 static const sal_Int32 spnAlignments[] = { XML_noControl, XML_left, XML_center, XML_distributed };
198 mnAlignment = STATIC_ARRAY_SELECT( spnAlignments, nAlignment, XML_left );
201 PhoneticSettings::PhoneticSettings( const WorkbookHelper& rHelper ) :
202 WorkbookHelper( rHelper )
206 void PhoneticSettings::importPhoneticPr( const AttributeList& rAttribs )
208 maModel.mnFontId = rAttribs.getInteger( XML_fontId, -1 );
209 maModel.mnType = rAttribs.getToken( XML_type, XML_fullwidthKatakana );
210 maModel.mnAlignment = rAttribs.getToken( XML_alignment, XML_left );
213 void PhoneticSettings::importPhoneticPr( SequenceInputStream& rStrm )
215 sal_uInt16 nFontId;
216 sal_Int32 nType, nAlignment;
217 nFontId = rStrm.readuInt16();
218 nType = rStrm.readInt32();
219 nAlignment = rStrm.readInt32();
220 maModel.mnFontId = nFontId;
221 maModel.setBiffData( nType, nAlignment );
224 void PhoneticSettings::importStringData( SequenceInputStream& rStrm )
226 sal_uInt16 nFontId, nFlags;
227 nFontId = rStrm.readuInt16();
228 nFlags = rStrm.readuInt16();
229 maModel.mnFontId = nFontId;
230 maModel.setBiffData( extractValue< sal_Int32 >( nFlags, 0, 2 ), extractValue< sal_Int32 >( nFlags, 2, 2 ) );
233 RichStringPhonetic::RichStringPhonetic() :
234 mnBasePos( -1 ),
235 mnBaseEnd( -1 )
239 void RichStringPhonetic::setText( const OUString& rText )
241 maText = rText;
244 void RichStringPhonetic::importPhoneticRun( const AttributeList& rAttribs )
246 mnBasePos = rAttribs.getInteger( XML_sb, -1 );
247 mnBaseEnd = rAttribs.getInteger( XML_eb, -1 );
250 void RichStringPhonetic::setBaseRange( sal_Int32 nBasePos, sal_Int32 nBaseEnd )
252 mnBasePos = nBasePos;
253 mnBaseEnd = nBaseEnd;
256 void PhoneticPortionModel::read( SequenceInputStream& rStrm )
258 mnPos = rStrm.readuInt16();
259 mnBasePos = rStrm.readuInt16();
260 mnBaseLen = rStrm.readuInt16();
263 void PhoneticPortionModelList::appendPortion( const PhoneticPortionModel& rPortion )
265 // same character index may occur several times
266 OSL_ENSURE( mvModels.empty() || ((mvModels.back().mnPos <= rPortion.mnPos) &&
267 (mvModels.back().mnBasePos + mvModels.back().mnBaseLen <= rPortion.mnBasePos)),
268 "PhoneticPortionModelList::appendPortion - wrong char order" );
269 if( mvModels.empty() || (mvModels.back().mnPos < rPortion.mnPos) )
271 mvModels.push_back( rPortion );
273 else if( mvModels.back().mnPos == rPortion.mnPos )
275 mvModels.back().mnBasePos = rPortion.mnBasePos;
276 mvModels.back().mnBaseLen = rPortion.mnBaseLen;
280 void PhoneticPortionModelList::importPortions( SequenceInputStream& rStrm )
282 sal_Int32 nCount = rStrm.readInt32();
283 mvModels.clear();
284 if( nCount > 0 )
286 mvModels.reserve( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 6 ) );
287 PhoneticPortionModel aPortion;
288 for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
290 aPortion.read( rStrm );
291 appendPortion( aPortion );
296 sal_Int32 RichString::importText(const AttributeList& rAttribs)
298 setAttributes(rAttribs);
300 return createPortion();
303 sal_Int32 RichString::importRun()
305 return createPortion();
308 void RichString::setAttributes(const AttributeList& rAttribs)
310 auto aAttrSpace = rAttribs.getString(oox::NMSP_xml | oox::XML_space);
311 if (aAttrSpace && *aAttrSpace == "preserve")
312 mbPreserveSpace = true;
315 RichStringPhoneticRef RichString::importPhoneticRun( const AttributeList& rAttribs )
317 RichStringPhoneticRef xPhonetic = createPhonetic();
318 xPhonetic->importPhoneticRun( rAttribs );
319 return xPhonetic;
322 void RichString::importPhoneticPr( const AttributeList& rAttribs, const WorkbookHelper& rHelper )
324 if (!mxPhonSettings)
325 mxPhonSettings.reset(new PhoneticSettings(rHelper));
326 mxPhonSettings->importPhoneticPr( rAttribs );
329 void RichString::importString( SequenceInputStream& rStrm, bool bRich, const WorkbookHelper& rHelper )
331 sal_uInt8 nFlags = bRich ? rStrm.readuInt8() : 0;
332 OUString aBaseText = BiffHelper::readString( rStrm );
334 if( !rStrm.isEof() && getFlag( nFlags, BIFF12_STRINGFLAG_FONTS ) )
336 FontPortionModelList aPortions;
337 aPortions.importPortions( rStrm );
338 createTextPortions( aBaseText, aPortions );
340 else
342 getPortion(createPortion()).setText( aBaseText );
345 if( !rStrm.isEof() && getFlag( nFlags, BIFF12_STRINGFLAG_PHONETICS ) )
347 OUString aPhoneticText = BiffHelper::readString( rStrm );
348 PhoneticPortionModelList aPortions;
349 aPortions.importPortions( rStrm );
350 if (!mxPhonSettings)
351 mxPhonSettings.reset(new PhoneticSettings(rHelper));
352 mxPhonSettings->importStringData( rStrm );
353 createPhoneticPortions( aPhoneticText, aPortions, aBaseText.getLength() );
357 void RichString::finalizeImport(const WorkbookHelper& rHelper)
359 for (RichStringPortion& rPortion : maTextPortions)
360 rPortion.finalizeImport( rHelper );
363 bool RichString::extractPlainString( OUString& orString, const oox::xls::Font* pFirstPortionFont ) const
365 if( !maPhonPortions.empty() )
366 return false;
367 if( maTextPortions.empty() )
369 orString.clear();
370 return true;
372 if( (maTextPortions.size() == 1) && !maTextPortions.front().hasFont() && !lclNeedsRichTextFormat( pFirstPortionFont ) )
374 orString = maTextPortions.front().getText();
375 return orString.indexOf( '\x0A' ) < 0;
377 return false;
380 void RichString::convert( const Reference< XText >& rxText )
382 if (maTextPortions.size() == 1)
384 // Set text directly to the cell when the string has only one portion.
385 // It's much faster this way.
386 const RichStringPortion& rPtn = maTextPortions.front();
387 rxText->setString(rPtn.getText());
388 rPtn.writeFontProperties(rxText);
389 return;
392 bool bReplaceOld = true;
393 for( auto& rTextPortion : maTextPortions )
395 rTextPortion.convert( rxText, bReplaceOld );
396 bReplaceOld = false; // do not replace first portion text with following portions
400 OUString RichString::getStringContent() const
402 OUStringBuffer sString;
403 for( auto& rTextPortion : maTextPortions )
404 sString.append(rTextPortion.getText());
405 return sString.makeStringAndClear();
408 std::unique_ptr<EditTextObject> RichString::convert( ScEditEngineDefaulter& rEE, const oox::xls::Font* pFirstPortionFont )
410 ESelection aSelection;
412 OUString sString(getStringContent());
414 // fdo#84370 - diving into editeng is not thread safe.
415 SolarMutexGuard aGuard;
417 rEE.SetTextCurrentDefaults(sString);
419 for( auto& rTextPortion : maTextPortions )
421 rTextPortion.convert( rEE, aSelection, pFirstPortionFont );
422 pFirstPortionFont = nullptr;
425 return rEE.CreateTextObject();
428 // private --------------------------------------------------------------------
430 sal_Int32 RichString::createPortion()
432 maTextPortions.emplace_back();
433 return maTextPortions.size() - 1;
436 RichStringPhoneticRef RichString::createPhonetic()
438 RichStringPhoneticRef xPhonetic = std::make_shared<RichStringPhonetic>();
439 maPhonPortions.push_back( xPhonetic );
440 return xPhonetic;
443 void RichString::createTextPortions( std::u16string_view aText, FontPortionModelList& rPortions )
445 maTextPortions.clear();
446 if( aText.empty() )
447 return;
449 sal_Int32 nStrLen = aText.size();
450 // add leading and trailing string position to ease the following loop
451 if( rPortions.empty() || (rPortions.front().mnPos > 0) )
452 rPortions.insert( rPortions.begin(), FontPortionModel( 0 ) );
453 if( rPortions.back().mnPos < nStrLen )
454 rPortions.push_back( FontPortionModel( nStrLen ) );
456 // create all string portions according to the font id vector
457 for( ::std::vector< FontPortionModel >::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
459 sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
460 if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
462 RichStringPortion& rPortion = getPortion(createPortion());
463 rPortion.setText( OUString(aText.substr( aIt->mnPos, nPortionLen )) );
464 rPortion.setFontId( aIt->mnFontId );
469 void RichString::createPhoneticPortions( std::u16string_view aText, PhoneticPortionModelList& rPortions, sal_Int32 nBaseLen )
471 maPhonPortions.clear();
472 if( aText.empty())
473 return;
475 sal_Int32 nStrLen = aText.size();
476 // no portions - assign phonetic text to entire base text
477 if( rPortions.empty() )
478 rPortions.push_back( PhoneticPortionModel( 0, 0, nBaseLen ) );
479 // add trailing string position to ease the following loop
480 if( rPortions.back().mnPos < nStrLen )
481 rPortions.push_back( PhoneticPortionModel( nStrLen, nBaseLen, 0 ) );
483 // create all phonetic portions according to the portions vector
484 for( ::std::vector< PhoneticPortionModel >::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
486 sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
487 if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
489 RichStringPhoneticRef xPhonetic = createPhonetic();
490 xPhonetic->setText( OUString(aText.substr( aIt->mnPos, nPortionLen )) );
491 xPhonetic->setBaseRange( aIt->mnBasePos, aIt->mnBasePos + aIt->mnBaseLen );
496 } // namespace oox::xls
498 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */