Move setting of LD_LIBRARY_PATH closer to invocation of cppunittester
[LibreOffice.git] / xmloff / source / transform / TransformerBase.cxx
blob7c2d0807a3c2c134c13e20083426ce37dbf59308
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 <rtl/ref.hxx>
21 #include <rtl/ustrbuf.hxx>
22 #include <tools/UnitConversion.hxx>
23 #include <osl/diagnose.h>
24 #include <com/sun/star/i18n/CharacterClassification.hpp>
25 #include <com/sun/star/i18n/UnicodeType.hpp>
26 #include <com/sun/star/util/MeasureUnit.hpp>
27 #include <sax/tools/converter.hxx>
28 #include <comphelper/processfactory.hxx>
29 #include <xmloff/namespacemap.hxx>
30 #include <xmloff/xmlnamespace.hxx>
31 #include "IgnoreTContext.hxx"
32 #include "RenameElemTContext.hxx"
33 #include "ProcAttrTContext.hxx"
34 #include "ProcAddAttrTContext.hxx"
35 #include "MergeElemTContext.hxx"
36 #include "CreateElemTContext.hxx"
37 #include "MutableAttrList.hxx"
38 #include "TransformerActions.hxx"
39 #include "ElemTransformerAction.hxx"
40 #include "PropertyActionsOOo.hxx"
41 #include "TransformerTokenMap.hxx"
43 #include "TransformerBase.hxx"
44 #include <xmloff/xmlimp.hxx>
46 using namespace ::xmloff::token;
47 using namespace ::com::sun::star;
48 using namespace ::com::sun::star::uno;
49 using namespace ::com::sun::star::beans;
50 using namespace ::com::sun::star::lang;
51 using namespace ::com::sun::star::i18n;
52 using namespace ::com::sun::star::xml::sax;
54 namespace
56 bool lcl_ConvertAttr( OUString & rOutAttribute, sal_Int32 nParam )
58 bool bResult = false;
59 enum XMLTokenEnum eTokenToRename =
60 static_cast< enum XMLTokenEnum >( nParam & 0xffff );
61 if( eTokenToRename != XML_TOKEN_INVALID &&
62 IsXMLToken( rOutAttribute, eTokenToRename ))
64 enum XMLTokenEnum eReplacementToken =
65 static_cast< enum XMLTokenEnum >( nParam >> 16 );
66 rOutAttribute = GetXMLToken( eReplacementToken );
67 bResult = true;
69 return bResult;
71 } // anonymous namespace
73 XMLTransformerContext *XMLTransformerBase::CreateContext( sal_uInt16 nPrefix,
74 const OUString& rLocalName, const OUString& rQName )
76 XMLTransformerActions::key_type aKey( nPrefix, rLocalName );
77 XMLTransformerActions::const_iterator aIter =
78 GetElemActions().find( aKey );
80 if( aIter != GetElemActions().end() )
82 sal_uInt32 nActionType = (*aIter).second.m_nActionType;
83 if( (nActionType & XML_ETACTION_USER_DEFINED) != 0 )
85 XMLTransformerContext *pContext =
86 CreateUserDefinedContext( (*aIter).second,
87 rQName );
88 OSL_ENSURE( pContext && !pContext->IsPersistent(),
89 "unknown or not persistent action" );
90 return pContext;
93 switch( nActionType )
95 case XML_ETACTION_COPY_CONTENT:
96 return new XMLIgnoreTransformerContext( *this, rQName, false,
97 false );
98 case XML_ETACTION_COPY:
99 return new XMLTransformerContext( *this, rQName );
100 case XML_ETACTION_RENAME_ELEM:
101 return new XMLRenameElemTransformerContext( *this, rQName,
102 (*aIter).second.GetQNamePrefixFromParam1(),
103 (*aIter).second.GetQNameTokenFromParam1() );
104 case XML_ETACTION_RENAME_ELEM_ADD_ATTR:
105 return new XMLRenameElemTransformerContext( *this, rQName,
106 (*aIter).second.GetQNamePrefixFromParam1(),
107 (*aIter).second.GetQNameTokenFromParam1(),
108 (*aIter).second.GetQNamePrefixFromParam2(),
109 (*aIter).second.GetQNameTokenFromParam2(),
110 static_cast< XMLTokenEnum >( (*aIter).second.m_nParam3 ) );
111 case XML_ETACTION_RENAME_ELEM_PROC_ATTRS:
112 return new XMLProcAttrTransformerContext( *this, rQName,
113 (*aIter).second.GetQNamePrefixFromParam1(),
114 (*aIter).second.GetQNameTokenFromParam1(),
115 static_cast< sal_uInt16 >( (*aIter).second.m_nParam2 ) );
116 case XML_ETACTION_RENAME_ELEM_ADD_PROC_ATTR:
117 return new XMLProcAddAttrTransformerContext( *this, rQName,
118 (*aIter).second.GetQNamePrefixFromParam1(),
119 (*aIter).second.GetQNameTokenFromParam1(),
120 static_cast< sal_uInt16 >(
121 (*aIter).second.m_nParam3 >> 16 ),
122 (*aIter).second.GetQNamePrefixFromParam2(),
123 (*aIter).second.GetQNameTokenFromParam2(),
124 static_cast< XMLTokenEnum >(
125 (*aIter).second.m_nParam3 & 0xffff ) );
126 case XML_ETACTION_RENAME_ELEM_PROC_ATTRS_COND:
128 const XMLTransformerContext *pCurrent = GetCurrentContext();
129 if( pCurrent->HasQName(
130 (*aIter).second.GetQNamePrefixFromParam3(),
131 (*aIter).second.GetQNameTokenFromParam3() ) )
132 return new XMLProcAttrTransformerContext( *this, rQName,
133 (*aIter).second.GetQNamePrefixFromParam1(),
134 (*aIter).second.GetQNameTokenFromParam1(),
135 static_cast< sal_uInt16 >( (*aIter).second.m_nParam2 ) );
136 else
137 return new XMLProcAttrTransformerContext( *this, rQName,
138 static_cast< sal_uInt16 >( (*aIter).second.m_nParam2 ) );
140 case XML_ETACTION_PROC_ATTRS:
141 return new XMLProcAttrTransformerContext( *this, rQName,
142 static_cast< sal_uInt16 >( (*aIter).second.m_nParam1 ) );
143 case XML_ETACTION_PROC_ATTRS_COND:
145 const XMLTransformerContext *pCurrent = GetCurrentContext();
146 if( pCurrent->HasQName(
147 (*aIter).second.GetQNamePrefixFromParam1(),
148 (*aIter).second.GetQNameTokenFromParam1() ) )
149 return new XMLProcAttrTransformerContext( *this, rQName,
150 static_cast< sal_uInt16 >( (*aIter).second.m_nParam2 ) );
152 break;
153 case XML_ETACTION_MOVE_ATTRS_TO_ELEMS:
154 return new XMLCreateElemTransformerContext( *this, rQName,
155 static_cast< sal_uInt16 >( (*aIter).second.m_nParam1 ) );
156 case XML_ETACTION_MOVE_ELEMS_TO_ATTRS:
157 return new XMLMergeElemTransformerContext( *this, rQName,
158 static_cast< sal_uInt16 >( (*aIter).second.m_nParam1 ) );
159 default:
160 OSL_ENSURE( false, "unknown action" );
161 break;
165 // default is copying
166 return new XMLTransformerContext( *this, rQName );
169 XMLTransformerActions *XMLTransformerBase::GetUserDefinedActions( sal_uInt16 )
171 return nullptr;
174 XMLTransformerBase::XMLTransformerBase( XMLTransformerActionInit const *pInit,
175 ::xmloff::token::XMLTokenEnum const *pTKMapInit )
176 noexcept :
177 m_pNamespaceMap( new SvXMLNamespaceMap ),
178 m_ElemActions( pInit ),
179 m_TokenMap( pTKMapInit )
181 GetNamespaceMap().Add( GetXMLToken(XML_NP_XLINK), GetXMLToken(XML_N_XLINK), XML_NAMESPACE_XLINK );
182 GetNamespaceMap().Add( GetXMLToken(XML_NP_DC), GetXMLToken(XML_N_DC), XML_NAMESPACE_DC );
183 GetNamespaceMap().Add( GetXMLToken(XML_NP_MATH), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH );
184 GetNamespaceMap().Add( GetXMLToken(XML_NP_OOO), GetXMLToken(XML_N_OOO), XML_NAMESPACE_OOO );
185 GetNamespaceMap().Add( GetXMLToken(XML_NP_DOM), GetXMLToken(XML_N_DOM), XML_NAMESPACE_DOM );
186 GetNamespaceMap().Add( GetXMLToken(XML_NP_OOOW), GetXMLToken(XML_N_OOOW), XML_NAMESPACE_OOOW );
187 GetNamespaceMap().Add( GetXMLToken(XML_NP_OOOC), GetXMLToken(XML_N_OOOC), XML_NAMESPACE_OOOC );
190 XMLTransformerBase::~XMLTransformerBase() noexcept
194 void SAL_CALL XMLTransformerBase::startDocument()
196 m_xHandler->startDocument();
199 void SAL_CALL XMLTransformerBase::endDocument()
201 m_xHandler->endDocument();
204 void SAL_CALL XMLTransformerBase::startElement( const OUString& rName,
205 const Reference< XAttributeList >& rAttrList )
207 std::unique_ptr<SvXMLNamespaceMap> pRewindMap;
209 // Process namespace attributes. This must happen before creating the
210 // context, because namespace declaration apply to the element name itself.
211 rtl::Reference<XMLMutableAttributeList> pMutableAttrList;
212 Reference< XAttributeList > xAttrList( rAttrList );
213 sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
214 for( sal_Int16 i=0; i < nAttrCount; i++ )
216 const OUString aAttrName = xAttrList->getNameByIndex( i );
217 if( ( aAttrName.getLength() >= 5 ) &&
218 ( aAttrName.startsWith( GetXMLToken(XML_XMLNS) ) ) &&
219 ( aAttrName.getLength() == 5 || ':' == aAttrName[5] ) )
221 if( !pRewindMap )
223 pRewindMap = std::move(m_pNamespaceMap);
224 m_pNamespaceMap.reset( new SvXMLNamespaceMap( *pRewindMap ) );
226 const OUString aAttrValue = xAttrList->getValueByIndex( i );
228 OUString aPrefix( ( aAttrName.getLength() == 5 )
229 ? OUString()
230 : aAttrName.copy( 6 ) );
231 // Add namespace, but only if it is known.
232 sal_uInt16 nKey = m_pNamespaceMap->AddIfKnown( aPrefix, aAttrValue );
233 // If namespace is unknown, try to match a name with similar
234 // TC Id and version
235 if( XML_NAMESPACE_UNKNOWN == nKey )
237 OUString aTestName( aAttrValue );
238 if( SvXMLNamespaceMap::NormalizeOasisURN( aTestName ) )
239 nKey = m_pNamespaceMap->AddIfKnown( aPrefix, aTestName );
241 // If that namespace is not known, too, add it as unknown
242 if( XML_NAMESPACE_UNKNOWN == nKey )
243 nKey = m_pNamespaceMap->Add( aPrefix, aAttrValue );
245 const OUString& rRepName = m_vReplaceNamespaceMap.GetNameByKey( nKey );
246 if( !rRepName.isEmpty() )
248 if( !pMutableAttrList )
250 pMutableAttrList = new XMLMutableAttributeList( xAttrList );
251 xAttrList = pMutableAttrList;
254 pMutableAttrList->SetValueByIndex( i, rRepName );
259 // Get element's namespace and local name.
260 OUString aLocalName;
261 sal_uInt16 nPrefix =
262 m_pNamespaceMap->GetKeyByAttrName( rName, &aLocalName );
264 // If there are contexts already, call a CreateChildContext at the topmost
265 // context. Otherwise, create a default context.
266 ::rtl::Reference < XMLTransformerContext > xContext;
267 if( !m_vContexts.empty() )
269 xContext = m_vContexts.back()->CreateChildContext( nPrefix,
270 aLocalName,
271 rName,
272 xAttrList );
274 else
276 xContext = CreateContext( nPrefix, aLocalName, rName );
279 OSL_ENSURE( xContext.is(), "XMLTransformerBase::startElement: missing context" );
280 if( !xContext.is() )
281 xContext = new XMLTransformerContext( *this, rName );
283 // Remember old namespace map.
284 if( pRewindMap )
285 xContext->PutRewindMap( std::move(pRewindMap) );
287 // Push context on stack.
288 m_vContexts.push_back( xContext );
290 // Call a startElement at the new context.
291 xContext->StartElement( xAttrList );
294 void SAL_CALL XMLTransformerBase::endElement( const OUString&
295 #if OSL_DEBUG_LEVEL > 0
296 rName
297 #endif
300 if( m_vContexts.empty() )
301 return;
303 // Get topmost context
304 ::rtl::Reference< XMLTransformerContext > xContext = m_vContexts.back();
306 #if OSL_DEBUG_LEVEL > 0
307 OSL_ENSURE( xContext->GetQName() == rName,
308 "XMLTransformerBase::endElement: popped context has wrong lname" );
309 #endif
311 // Call a EndElement at the current context.
312 xContext->EndElement();
314 // and remove it from the stack.
315 m_vContexts.pop_back();
317 // Get a namespace map to rewind.
318 std::unique_ptr<SvXMLNamespaceMap> pRewindMap = xContext->TakeRewindMap();
320 // Delete the current context.
321 xContext = nullptr;
323 // Rewind a namespace map.
324 if( pRewindMap )
326 m_pNamespaceMap = std::move( pRewindMap );
330 void SAL_CALL XMLTransformerBase::characters( const OUString& rChars )
332 if( !m_vContexts.empty() )
334 m_vContexts.back()->Characters( rChars );
338 void SAL_CALL XMLTransformerBase::ignorableWhitespace( const OUString& rWhitespaces )
340 m_xHandler->ignorableWhitespace( rWhitespaces );
343 void SAL_CALL XMLTransformerBase::processingInstruction( const OUString& rTarget,
344 const OUString& rData )
346 m_xHandler->processingInstruction( rTarget, rData );
349 void SAL_CALL XMLTransformerBase::setDocumentLocator( const Reference< XLocator >& )
353 // XExtendedDocumentHandler
354 void SAL_CALL XMLTransformerBase::startCDATA()
358 void SAL_CALL XMLTransformerBase::endCDATA()
362 void SAL_CALL XMLTransformerBase::comment( const OUString& /*rComment*/ )
366 void SAL_CALL XMLTransformerBase::allowLineBreak()
370 void SAL_CALL XMLTransformerBase::unknown( const OUString& /*rString*/ )
374 // XInitialize
375 void SAL_CALL XMLTransformerBase::initialize( const Sequence< Any >& aArguments )
377 for( const auto& rArgument : aArguments )
379 // use isAssignableFrom instead of comparing the types to
380 // allow XExtendedDocumentHandler instead of XDocumentHandler (used in
381 // writeOasis2OOoLibraryElement in sfx2).
382 // The Any shift operator can't be used to query the type because it
383 // uses queryInterface, and the model also has a XPropertySet interface.
385 css::uno::Reference< XFastDocumentHandler > xFastHandler;
386 if( (rArgument >>= xFastHandler) && xFastHandler )
388 SvXMLImport *pFastHandler = static_cast<SvXMLImport*>( xFastHandler.get() );
389 m_xHandler.set( new SvXMLLegacyToFastDocHandler( pFastHandler ) );
391 // document handler
392 else if( cppu::UnoType<XDocumentHandler>::get().isAssignableFrom( rArgument.getValueType() ) )
394 m_xHandler.set( rArgument, UNO_QUERY );
396 // property set to transport data across
397 else if( cppu::UnoType<XPropertySet>::get().isAssignableFrom( rArgument.getValueType() ) )
398 m_xPropSet.set( rArgument, UNO_QUERY );
399 // xmodel
400 else if( cppu::UnoType<css::frame::XModel>::get().isAssignableFrom( rArgument.getValueType() ) )
401 mxModel.set( rArgument, UNO_QUERY );
404 if( m_xPropSet.is() )
406 Any aAny;
407 OUString sRelPath, sName;
408 Reference< XPropertySetInfo > xPropSetInfo =
409 m_xPropSet->getPropertySetInfo();
410 OUString sPropName( u"StreamRelPath"_ustr );
411 if( xPropSetInfo->hasPropertyByName(sPropName) )
413 aAny = m_xPropSet->getPropertyValue(sPropName);
414 aAny >>= sRelPath;
416 sPropName = "StreamName";
417 if( xPropSetInfo->hasPropertyByName(sPropName) )
419 aAny = m_xPropSet->getPropertyValue(sPropName);
420 aAny >>= sName;
422 if( !sName.isEmpty() )
424 m_aExtPathPrefix = "../";
426 // If there is a rel path within a package, then append
427 // additional '../'. If the rel path contains an ':', then it is
428 // an absolute URI (or invalid URI, because zip files don't
429 // permit ':'), and it will be ignored.
430 if( !sRelPath.isEmpty() )
432 sal_Int32 nColPos = sRelPath.indexOf( ':' );
433 OSL_ENSURE( -1 == nColPos,
434 "StreamRelPath contains ':', absolute URI?" );
436 if( -1 == nColPos )
438 OUString sTmp = m_aExtPathPrefix;
439 sal_Int32 nPos = 0;
442 m_aExtPathPrefix += sTmp;
443 nPos = sRelPath.indexOf( '/', nPos + 1 );
445 while( -1 != nPos );
452 assert(m_xHandler.is()); // can't do anything without that
455 static sal_Int16 lcl_getUnit( std::u16string_view rValue )
457 if( o3tl::endsWithIgnoreAsciiCase( rValue, "cm" ) )
458 return util::MeasureUnit::CM;
459 else if ( o3tl::endsWithIgnoreAsciiCase( rValue, "mm" ) )
460 return util::MeasureUnit::MM;
461 else
462 return util::MeasureUnit::INCH;
465 XMLMutableAttributeList *XMLTransformerBase::ProcessAttrList(
466 Reference< XAttributeList >& rAttrList, sal_uInt16 nActionMap,
467 bool bClone )
469 rtl::Reference<XMLMutableAttributeList> pMutableAttrList;
470 XMLTransformerActions *pActions = GetUserDefinedActions( nActionMap );
471 OSL_ENSURE( pActions, "go no actions" );
472 if( pActions )
474 sal_Int16 nAttrCount = rAttrList.is() ? rAttrList->getLength() : 0;
475 for( sal_Int16 i=0; i < nAttrCount; ++i )
477 const OUString aAttrName = rAttrList->getNameByIndex( i );
478 const OUString aAttrValue = rAttrList->getValueByIndex( i );
479 OUString aLocalName;
480 sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName( aAttrName,
481 &aLocalName );
483 XMLTransformerActions::key_type aKey( nPrefix, aLocalName );
484 XMLTransformerActions::const_iterator aIter =
485 pActions->find( aKey );
486 if( aIter != pActions->end() )
488 if( !pMutableAttrList )
490 pMutableAttrList = new XMLMutableAttributeList( rAttrList,
491 bClone );
492 rAttrList = pMutableAttrList;
495 sal_uInt32 nAction = (*aIter).second.m_nActionType;
496 bool bRename = false;
497 switch( nAction )
499 case XML_ATACTION_RENAME:
500 bRename = true;
501 break;
502 case XML_ATACTION_COPY:
503 break;
504 case XML_ATACTION_REMOVE:
505 case XML_ATACTION_STYLE_DISPLAY_NAME:
506 pMutableAttrList->RemoveAttributeByIndex( i );
507 --i;
508 --nAttrCount;
509 break;
510 case XML_ATACTION_RENAME_IN2INCH:
511 bRename = true;
512 [[fallthrough]];
513 case XML_ATACTION_IN2INCH:
515 OUString aAttrValue2( aAttrValue );
516 if( ReplaceSingleInWithInch( aAttrValue2 ) )
517 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
519 break;
520 case XML_ATACTION_INS2INCHS:
522 OUString aAttrValue2( aAttrValue );
523 if( ReplaceInWithInch( aAttrValue2 ) )
524 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
526 break;
527 case XML_ATACTION_RENAME_INCH2IN:
528 bRename = true;
529 [[fallthrough]];
530 case XML_ATACTION_INCH2IN:
532 OUString aAttrValue2( aAttrValue );
533 if( ReplaceSingleInchWithIn( aAttrValue2 ) )
534 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
536 break;
537 case XML_ATACTION_INCHS2INS:
539 OUString aAttrValue2( aAttrValue );
540 if( ReplaceInchWithIn( aAttrValue2 ) )
541 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
543 break;
544 case XML_ATACTION_TWIPS2IN:
546 OUString aAttrValue2( aAttrValue );
548 XMLTransformerBase::ReplaceSingleInchWithIn( aAttrValue2 );
549 if( isWriter() )
551 sal_Int16 const nDestUnit = lcl_getUnit(aAttrValue2);
553 // convert twips value to inch
554 sal_Int32 nMeasure;
555 if (::sax::Converter::convertMeasure(nMeasure,
556 aAttrValue2))
558 nMeasure = static_cast<sal_Int32>(convertTwipToMm100(nMeasure));
560 OUStringBuffer aBuffer;
561 ::sax::Converter::convertMeasure(aBuffer,
562 nMeasure, util::MeasureUnit::MM_100TH,
563 nDestUnit );
564 aAttrValue2 = aBuffer.makeStringAndClear();
568 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
570 break;
571 case XML_ATACTION_RENAME_DECODE_STYLE_NAME_REF:
572 bRename = true;
573 [[fallthrough]];
574 case XML_ATACTION_DECODE_STYLE_NAME:
575 case XML_ATACTION_DECODE_STYLE_NAME_REF:
577 OUString aAttrValue2( aAttrValue );
578 if( DecodeStyleName(aAttrValue2) )
579 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
581 break;
582 case XML_ATACTION_ENCODE_STYLE_NAME:
584 OUString aAttrValue2( aAttrValue );
585 if( EncodeStyleName(aAttrValue2) )
587 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
588 OUString aNewAttrQName(
589 GetNamespaceMap().GetQNameByKey(
590 nPrefix,
591 ::xmloff::token::GetXMLToken(
592 XML_DISPLAY_NAME ) ) );
593 pMutableAttrList->AddAttribute( aNewAttrQName,
594 aAttrValue );
597 break;
598 case XML_ATACTION_RENAME_ENCODE_STYLE_NAME_REF:
599 bRename = true;
600 [[fallthrough]];
601 case XML_ATACTION_ENCODE_STYLE_NAME_REF:
603 OUString aAttrValue2( aAttrValue );
604 if( EncodeStyleName(aAttrValue2) )
605 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
607 break;
608 case XML_ATACTION_RENAME_NEG_PERCENT:
609 bRename = true;
610 [[fallthrough]];
611 case XML_ATACTION_NEG_PERCENT:
613 OUString aAttrValue2( aAttrValue );
614 if( NegPercent( aAttrValue2 ) )
615 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
617 break;
618 case XML_ATACTION_RENAME_ADD_NAMESPACE_PREFIX:
619 bRename = true;
620 [[fallthrough]];
621 case XML_ATACTION_ADD_NAMESPACE_PREFIX:
623 OUString aAttrValue2( aAttrValue );
624 sal_uInt16 nValPrefix =
625 static_cast<sal_uInt16>(
626 bRename ? (*aIter).second.m_nParam2
627 : (*aIter).second.m_nParam1);
628 AddNamespacePrefix( aAttrValue2, nValPrefix );
629 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
631 break;
632 case XML_ATACTION_ADD_APP_NAMESPACE_PREFIX:
634 OUString aAttrValue2( aAttrValue );
635 sal_uInt16 nValPrefix =
636 static_cast<sal_uInt16>((*aIter).second.m_nParam1);
637 if( IsXMLToken( GetClass(), XML_SPREADSHEET ) )
638 nValPrefix = XML_NAMESPACE_OOOC;
639 else if( IsXMLToken( GetClass(), XML_TEXT ) )
640 nValPrefix = XML_NAMESPACE_OOOW;
641 AddNamespacePrefix( aAttrValue2, nValPrefix );
642 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
644 break;
645 case XML_ATACTION_RENAME_REMOVE_NAMESPACE_PREFIX:
646 bRename = true;
647 [[fallthrough]];
648 case XML_ATACTION_REMOVE_NAMESPACE_PREFIX:
650 OUString aAttrValue2( aAttrValue );
651 sal_uInt16 nValPrefix =
652 static_cast<sal_uInt16>(
653 bRename ? (*aIter).second.m_nParam2
654 : (*aIter).second.m_nParam1);
655 if( RemoveNamespacePrefix( aAttrValue2, nValPrefix ) )
656 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
658 break;
659 case XML_ATACTION_REMOVE_ANY_NAMESPACE_PREFIX:
661 OUString aAttrValue2( aAttrValue );
662 if( RemoveNamespacePrefix( aAttrValue2 ) )
663 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
665 break;
666 case XML_ATACTION_URI_OOO:
668 OUString aAttrValue2( aAttrValue );
669 if( ConvertURIToOASIS( aAttrValue2,
670 static_cast< bool >((*aIter).second.m_nParam1)))
671 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
673 break;
674 case XML_ATACTION_URI_OASIS:
676 OUString aAttrValue2( aAttrValue );
677 if( ConvertURIToOOo( aAttrValue2,
678 static_cast< bool >((*aIter).second.m_nParam1)))
679 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
681 break;
682 case XML_ATACTION_RENAME_ATTRIBUTE:
684 OUString aAttrValue2( aAttrValue );
685 RenameAttributeValue(
686 aAttrValue2,
687 (*aIter).second.m_nParam1,
688 (*aIter).second.m_nParam2,
689 (*aIter).second.m_nParam3 );
690 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
692 break;
693 case XML_ATACTION_RNG2ISO_DATETIME:
695 OUString aAttrValue2( aAttrValue );
696 if( ConvertRNGDateTimeToISO( aAttrValue2 ))
697 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
699 break;
700 case XML_ATACTION_RENAME_RNG2ISO_DATETIME:
702 OUString aAttrValue2( aAttrValue );
703 if( ConvertRNGDateTimeToISO( aAttrValue2 ))
704 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
705 bRename = true;
707 break;
708 case XML_ATACTION_IN2TWIPS:
710 OUString aAttrValue2( aAttrValue );
711 XMLTransformerBase::ReplaceSingleInWithInch( aAttrValue2 );
713 if( isWriter() )
715 sal_Int16 const nDestUnit = lcl_getUnit(aAttrValue2);
717 // convert inch value to twips and export as faked inch
718 sal_Int32 nMeasure;
719 if (::sax::Converter::convertMeasure(nMeasure,
720 aAttrValue2))
722 nMeasure = o3tl::toTwips(nMeasure, o3tl::Length::mm100);
724 OUStringBuffer aBuffer;
725 ::sax::Converter::convertMeasure( aBuffer,
726 nMeasure, util::MeasureUnit::MM_100TH,
727 nDestUnit );
728 aAttrValue2 = aBuffer.makeStringAndClear();
732 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
734 break;
735 case XML_ATACTION_SVG_WIDTH_HEIGHT_OOO:
737 OUString aAttrValue2( aAttrValue );
738 ReplaceSingleInchWithIn( aAttrValue2 );
740 sal_Int16 const nDestUnit = lcl_getUnit( aAttrValue2 );
742 sal_Int32 nMeasure;
743 if (::sax::Converter::convertMeasure(nMeasure,
744 aAttrValue2))
747 if( nMeasure > 0 )
748 nMeasure -= 1;
749 else if( nMeasure < 0 )
750 nMeasure += 1;
753 OUStringBuffer aBuffer;
754 ::sax::Converter::convertMeasure(aBuffer, nMeasure,
755 util::MeasureUnit::MM_100TH, nDestUnit);
756 aAttrValue2 = aBuffer.makeStringAndClear();
759 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
761 break;
762 case XML_ATACTION_SVG_WIDTH_HEIGHT_OASIS:
764 OUString aAttrValue2( aAttrValue );
765 ReplaceSingleInWithInch( aAttrValue2 );
767 sal_Int16 const nDestUnit = lcl_getUnit( aAttrValue2 );
769 sal_Int32 nMeasure;
770 if (::sax::Converter::convertMeasure(nMeasure,
771 aAttrValue2))
774 if( nMeasure > 0 )
775 nMeasure += 1;
776 else if( nMeasure < 0 )
777 nMeasure -= 1;
780 OUStringBuffer aBuffer;
781 ::sax::Converter::convertMeasure(aBuffer, nMeasure,
782 util::MeasureUnit::MM_100TH, nDestUnit );
783 aAttrValue2 = aBuffer.makeStringAndClear();
786 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
788 break;
789 case XML_ATACTION_DECODE_ID:
791 const sal_Int32 nLen = aAttrValue.getLength();
792 OUStringBuffer aBuffer;
794 sal_Int32 pos;
795 for( pos = 0; pos < nLen; pos++ )
797 sal_Unicode c = aAttrValue[pos];
798 if( (c >= '0') && (c <= '9') )
799 aBuffer.append( c );
800 else
801 aBuffer.append( static_cast<sal_Int32>(c) );
804 pMutableAttrList->SetValueByIndex( i, aBuffer.makeStringAndClear() );
806 break;
807 // #i50322# - special handling for the
808 // transparency of writer background graphics.
809 case XML_ATACTION_WRITER_BACK_GRAPHIC_TRANSPARENCY:
811 // determine, if it's the transparency of a document style
812 XMLTransformerContext* pFirstContext = m_vContexts[0].get();
813 OUString aFirstContextLocalName;
814 /* sal_uInt16 nFirstContextPrefix = */
815 GetNamespaceMap().GetKeyByAttrName( pFirstContext->GetQName(),
816 &aFirstContextLocalName );
817 bool bIsDocumentStyle(
818 ::xmloff::token::IsXMLToken( aFirstContextLocalName,
819 XML_DOCUMENT_STYLES ) );
820 // no conversion of transparency value for document
821 // styles, because former OpenOffice.org version writes
822 // writes always a transparency value of 100% and doesn't
823 // read the value. Thus, it's interpreted as 0%
824 if ( !bIsDocumentStyle )
826 OUString aAttrValue2( aAttrValue );
827 NegPercent(aAttrValue2);
828 pMutableAttrList->SetValueByIndex( i, aAttrValue2 );
830 bRename = true;
832 break;
833 case XML_ATACTION_SHAPEID:
835 OUString sNewValue = "shape" + aAttrValue;
836 pMutableAttrList->SetValueByIndex( i, sNewValue );
837 break;
840 default:
841 OSL_ENSURE( false, "unknown action" );
842 break;
845 if( bRename )
847 OUString aNewAttrQName(
848 GetNamespaceMap().GetQNameByKey(
849 (*aIter).second.GetQNamePrefixFromParam1(),
850 ::xmloff::token::GetXMLToken(
851 (*aIter).second.GetQNameTokenFromParam1()) ) );
852 pMutableAttrList->RenameAttributeByIndex( i,
853 aNewAttrQName );
859 return pMutableAttrList.get();
862 bool XMLTransformerBase::ReplaceSingleInchWithIn( OUString& rValue )
864 bool bRet = false;
865 sal_Int32 nPos = rValue.getLength();
866 while( nPos && rValue[nPos-1] <= ' ' )
867 --nPos;
868 if( nPos > 2 &&
869 ('c'==rValue[nPos-2] || 'C'==rValue[nPos-2]) &&
870 ('h'==rValue[nPos-1] || 'H'==rValue[nPos-1]) )
872 rValue =rValue.copy( 0, nPos-2 );
873 bRet = true;
876 return bRet;
879 bool XMLTransformerBase::ReplaceInchWithIn( OUString& rValue )
881 bool bRet = false;
882 sal_Int32 nPos = 1;
883 while( nPos < rValue.getLength()-3 )
885 sal_Unicode c = rValue[nPos];
886 if( 'i'==c || 'I'==c )
888 c = rValue[nPos-1];
889 if( (c >= '0' && c <= '9') || '.' == c )
891 c = rValue[nPos+1];
892 if( 'n'==c || 'N'==c )
894 c = rValue[nPos+2];
895 if( 'c'==c || 'C'==c )
897 c = rValue[nPos+3];
898 if( 'h'==c || 'H'==c )
900 rValue = rValue.replaceAt( nPos,
901 4, GetXMLToken(XML_IN) );
902 nPos += 2;
903 bRet = true;
904 continue;
910 ++nPos;
913 return bRet;
916 bool XMLTransformerBase::ReplaceSingleInWithInch( OUString& rValue )
918 bool bRet = false;
920 sal_Int32 nPos = rValue.getLength();
921 while( nPos && rValue[nPos-1] <= ' ' )
922 --nPos;
923 if( nPos > 2 &&
924 ('i'==rValue[nPos-2] ||
925 'I'==rValue[nPos-2]) &&
926 ('n'==rValue[nPos-1] ||
927 'N'==rValue[nPos-1]) )
929 nPos -= 2;
930 rValue = rValue.replaceAt( nPos, rValue.getLength() - nPos,
931 GetXMLToken(XML_INCH) );
932 bRet = true;
935 return bRet;
938 bool XMLTransformerBase::ReplaceInWithInch( OUString& rValue )
940 bool bRet = false;
941 sal_Int32 nPos = 1;
942 while( nPos < rValue.getLength()-1 )
944 sal_Unicode c = rValue[nPos];
945 if( 'i'==c || 'I'==c )
947 c = rValue[nPos-1];
948 if( (c >= '0' && c <= '9') || '.' == c )
950 c = rValue[nPos+1];
951 if( 'n'==c || 'N'==c )
953 rValue = rValue.replaceAt( nPos,
954 2, GetXMLToken(XML_INCH) );
955 nPos += 4;
956 bRet = true;
957 continue;
961 ++nPos;
964 return bRet;
967 bool XMLTransformerBase::EncodeStyleName( OUString& rName ) const
969 static const char aHexTab[] = "0123456789abcdef";
971 bool bEncoded = false;
973 sal_Int32 nLen = rName.getLength();
974 OUStringBuffer aBuffer( nLen );
976 for( sal_Int32 i = 0; i < nLen; i++ )
978 sal_Unicode c = rName[i];
979 bool bValidChar = false;
980 if( c < 0x00ffU )
982 bValidChar =
983 (c >= 0x0041 && c <= 0x005a) ||
984 (c >= 0x0061 && c <= 0x007a) ||
985 (c >= 0x00c0 && c <= 0x00d6) ||
986 (c >= 0x00d8 && c <= 0x00f6) ||
987 (c >= 0x00f8 && c <= 0x00ff) ||
988 ( i > 0 && ( (c >= 0x0030 && c <= 0x0039) ||
989 c == 0x00b7 || c == '-' || c == '.') );
991 else
993 if( (c >= 0xf900U && c <= 0xfffeU) ||
994 (c >= 0x20ddU && c <= 0x20e0U))
996 bValidChar = false;
998 else if( (c >= 0x02bbU && c <= 0x02c1U) || c == 0x0559 ||
999 c == 0x06e5 || c == 0x06e6 )
1001 bValidChar = true;
1003 else if( c == 0x0387 )
1005 bValidChar = i > 0;
1007 else
1009 if( !xCharClass.is() )
1011 const_cast < XMLTransformerBase * >(this)
1012 ->xCharClass = CharacterClassification::create( comphelper::getProcessComponentContext() );
1014 sal_Int16 nType = xCharClass->getType( rName, i );
1016 switch( nType )
1018 case UnicodeType::UPPERCASE_LETTER: // Lu
1019 case UnicodeType::LOWERCASE_LETTER: // Ll
1020 case UnicodeType::TITLECASE_LETTER: // Lt
1021 case UnicodeType::OTHER_LETTER: // Lo
1022 case UnicodeType::LETTER_NUMBER: // Nl
1023 bValidChar = true;
1024 break;
1025 case UnicodeType::NON_SPACING_MARK: // Ms
1026 case UnicodeType::ENCLOSING_MARK: // Me
1027 case UnicodeType::COMBINING_SPACING_MARK: //Mc
1028 case UnicodeType::MODIFIER_LETTER: // Lm
1029 case UnicodeType::DECIMAL_DIGIT_NUMBER: // Nd
1030 bValidChar = i > 0;
1031 break;
1035 if( bValidChar )
1037 aBuffer.append( c );
1039 else
1041 aBuffer.append( '_' );
1042 if( c > 0x0fff )
1043 aBuffer.append( static_cast< sal_Unicode >(
1044 aHexTab[ (c >> 12) & 0x0f ] ) );
1045 if( c > 0x00ff )
1046 aBuffer.append( static_cast< sal_Unicode >(
1047 aHexTab[ (c >> 8) & 0x0f ] ) );
1048 if( c > 0x000f )
1049 aBuffer.append( static_cast< sal_Unicode >(
1050 aHexTab[ (c >> 4) & 0x0f ] ) );
1051 aBuffer.append(
1052 OUString::number(static_cast< sal_Unicode >( aHexTab[ c & 0x0f ] ) )
1053 + "_" );
1054 bEncoded = true;
1058 if( aBuffer.getLength() > (1<<15)-1 )
1059 bEncoded = false;
1061 if( bEncoded )
1062 rName = aBuffer.makeStringAndClear();
1063 return bEncoded;
1066 bool XMLTransformerBase::DecodeStyleName( OUString& rName )
1068 bool bEncoded = false;
1070 sal_Int32 nLen = rName.getLength();
1071 OUStringBuffer aBuffer( nLen );
1073 bool bWithinHex = false;
1074 sal_Unicode cEnc = 0;
1075 for( sal_Int32 i = 0; i < nLen; i++ )
1077 sal_Unicode c = rName[i];
1078 if( '_' == c )
1080 if( bWithinHex )
1082 aBuffer.append( cEnc );
1083 cEnc = 0;
1085 else
1087 bEncoded = true;
1089 bWithinHex = !bWithinHex;
1091 else if( bWithinHex )
1093 sal_Unicode cDigit;
1094 if( c >= '0' && c <= '9' )
1096 cDigit = c - '0';
1098 else if( c >= 'a' && c <= 'f' )
1100 cDigit = c - 'a' + 10;
1102 else if( c >= 'A' && c <= 'F' )
1104 cDigit = c - 'A' + 10;
1106 else
1108 // error
1109 bEncoded = false;
1110 break;
1112 cEnc = (cEnc << 4) + cDigit;
1114 else
1116 aBuffer.append( c );
1120 if( bEncoded )
1121 rName = aBuffer.makeStringAndClear();
1122 return bEncoded;
1125 bool XMLTransformerBase::NegPercent( OUString& rValue )
1127 bool bRet = false;
1128 bool bNeg = false;
1129 double nVal = 0;
1131 sal_Int32 nPos = 0;
1132 sal_Int32 nLen = rValue.getLength();
1134 // skip white space
1135 while( nPos < nLen && ' ' == rValue[nPos] )
1136 nPos++;
1138 if( nPos < nLen && '-' == rValue[nPos] )
1140 bNeg = true;
1141 nPos++;
1144 // get number
1145 while( nPos < nLen &&
1146 '0' <= rValue[nPos] &&
1147 '9' >= rValue[nPos] )
1149 // TODO: check overflow!
1150 nVal *= 10;
1151 nVal += (rValue[nPos] - '0');
1152 nPos++;
1154 if( nPos < nLen && '.' == rValue[nPos] )
1156 nPos++;
1157 double nDiv = 1.;
1159 while( nPos < nLen &&
1160 '0' <= rValue[nPos] &&
1161 '9' >= rValue[nPos] )
1163 // TODO: check overflow!
1164 nDiv *= 10;
1165 nVal += ( static_cast<double>(rValue[nPos] - '0') / nDiv );
1166 nPos++;
1170 // skip white space
1171 while( nPos < nLen && ' ' == rValue[nPos] )
1172 nPos++;
1174 if( nPos < nLen && '%' == rValue[nPos] )
1176 if( bNeg )
1177 nVal = -nVal;
1178 nVal += .5;
1180 sal_Int32 nIntVal = 100 - static_cast<sal_Int32>( nVal );
1182 rValue = OUString::number(nIntVal) + "%";
1184 bRet = true;
1187 return bRet;
1190 void XMLTransformerBase::AddNamespacePrefix( OUString& rName,
1191 sal_uInt16 nPrefix ) const
1193 rName = GetNamespaceMap().GetQNameByKey( nPrefix, rName, false );
1196 bool XMLTransformerBase::RemoveNamespacePrefix( OUString& rName,
1197 sal_uInt16 nPrefixOnly ) const
1199 OUString aLocalName;
1200 sal_uInt16 nPrefix =
1201 GetNamespaceMap().GetKeyByAttrValueQName(rName, &aLocalName);
1202 bool bRet = XML_NAMESPACE_UNKNOWN != nPrefix &&
1203 (USHRT_MAX == nPrefixOnly || nPrefix == nPrefixOnly);
1204 if( bRet )
1205 rName = aLocalName;
1207 return bRet;
1210 bool XMLTransformerBase::ConvertURIToOASIS( OUString& rURI,
1211 bool bSupportPackage ) const
1213 bool bRet = false;
1214 if( !m_aExtPathPrefix.isEmpty() && !rURI.isEmpty() )
1216 bool bRel = false;
1217 switch( rURI[0] )
1219 case '#':
1220 // no rel path, but
1221 // for package URIs, the '#' has to be removed
1222 if( bSupportPackage )
1224 rURI = rURI.copy( 1 );
1225 bRet = true;
1227 break;
1228 case '/':
1229 // no rel path; nothing to do
1230 break;
1231 case '.':
1232 // a rel path; to keep URI simple, remove './', if there
1233 bRel = true;
1234 if( rURI.getLength() > 1 && '/' == rURI[1] )
1236 rURI = rURI.copy( 2 );
1237 bRet = true;
1239 break;
1240 default:
1241 // check for a RFC2396 schema
1243 bRel = true;
1244 sal_Int32 nPos = 1;
1245 sal_Int32 nLen = rURI.getLength();
1246 while( nPos < nLen )
1248 switch( rURI[nPos] )
1250 case '/':
1251 // a relative path segment
1252 nPos = nLen; // leave loop
1253 break;
1254 case ':':
1255 // a schema
1256 bRel = false;
1257 nPos = nLen; // leave loop
1258 break;
1259 default:
1260 // we don't care about any other characters
1261 break;
1263 ++nPos;
1268 if( bRel )
1270 rURI = m_aExtPathPrefix + rURI;
1271 bRet = true;
1275 return bRet;
1278 bool XMLTransformerBase::ConvertURIToOOo( OUString& rURI,
1279 bool bSupportPackage ) const
1281 bool bRet = false;
1282 if( !rURI.isEmpty() )
1284 bool bPackage = false;
1285 switch( rURI[0] )
1287 case '/':
1288 // no rel path; nothing to do
1289 break;
1290 case '.':
1291 // a rel path
1292 if( rURI.startsWith( m_aExtPathPrefix ) )
1294 // an external URI; remove '../'
1295 rURI = rURI.copy( m_aExtPathPrefix.getLength() );
1296 bRet = true;
1298 else
1300 bPackage = true;
1302 break;
1303 default:
1304 // check for a RFC2396 schema
1306 bPackage = true;
1307 sal_Int32 nPos = 1;
1308 sal_Int32 nLen = rURI.getLength();
1309 while( nPos < nLen )
1311 switch( rURI[nPos] )
1313 case '/':
1314 // a relative path segment within the package
1315 nPos = nLen - 1; // leave loop
1316 break;
1317 case ':':
1318 // a schema
1319 bPackage = false;
1320 nPos = nLen - 1; // leave loop
1321 break;
1322 default:
1323 // we don't care about any other characters
1324 break;
1326 ++nPos;
1331 if( bPackage && bSupportPackage )
1333 if( rURI.startsWith( "./" ) )
1334 rURI = rURI.copy( 2 );
1335 rURI = "#" + rURI;
1336 bRet = true;
1340 return bRet;
1343 bool XMLTransformerBase::RenameAttributeValue(
1344 OUString& rOutAttributeValue,
1345 sal_Int32 nParam1,
1346 sal_Int32 nParam2,
1347 sal_Int32 nParam3 )
1349 return ( lcl_ConvertAttr( rOutAttributeValue, nParam1) ||
1350 lcl_ConvertAttr( rOutAttributeValue, nParam2) ||
1351 lcl_ConvertAttr( rOutAttributeValue, nParam3) );
1354 // static
1355 bool XMLTransformerBase::ConvertRNGDateTimeToISO( OUString& rDateTime )
1357 if( !rDateTime.isEmpty() &&
1358 rDateTime.indexOf( '.' ) != -1 )
1360 rDateTime = rDateTime.replace( '.', ',');
1361 return true;
1364 return false;
1367 XMLTokenEnum XMLTransformerBase::GetToken( const OUString& rStr ) const
1369 XMLTransformerTokenMap::const_iterator aIter =
1370 m_TokenMap.find( rStr );
1371 if( aIter == m_TokenMap.end() )
1372 return XML_TOKEN_END;
1373 else
1374 return (*aIter).second;
1378 const XMLTransformerContext *XMLTransformerBase::GetCurrentContext() const
1380 OSL_ENSURE( !m_vContexts.empty(), "empty stack" );
1383 return m_vContexts.empty() ? nullptr : m_vContexts.back().get();
1386 const XMLTransformerContext *XMLTransformerBase::GetAncestorContext(
1387 sal_uInt32 n ) const
1389 auto nSize = m_vContexts.size();
1391 OSL_ENSURE( nSize > n + 2 , "invalid context" );
1393 return nSize > n + 2 ? m_vContexts[nSize - (n + 2)].get() : nullptr;
1396 bool XMLTransformerBase::isWriter() const
1398 Reference< XServiceInfo > xSI( mxModel, UNO_QUERY );
1399 return xSI.is() &&
1400 ( xSI->supportsService(u"com.sun.star.text.TextDocument"_ustr) ||
1401 xSI->supportsService(u"com.sun.star.text.WebDocument"_ustr) ||
1402 xSI->supportsService(u"com.sun.star.text.GlobalDocument"_ustr) );
1405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */